From caf755a90796d9fa2a118c59e59f2aeee2ad6995 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 18:33:17 -0400 Subject: [PATCH 1/9] Move the CSS engine into experiments/css --- experiments/css/.gitignore | 3 + experiments/css/BUGS-from-ui-bridge.md | 154 + experiments/css/Cargo.lock | 623 ++ experiments/css/Cargo.toml | 34 + experiments/css/HANDOFF-compute-poison.md | 86 + experiments/css/README.md | 206 + .../css/crates/css-compiler/Cargo.toml | 16 + experiments/css/crates/css-compiler/build.rs | 122 + experiments/css/crates/css-compiler/c/shim.c | 799 +++ .../css-compiler/examples/compile_time.rs | 31 + .../crates/css-compiler/examples/events.rs | 60 + .../css/crates/css-compiler/src/alloc_shim.rs | 100 + .../css/crates/css-compiler/src/canonical.rs | 236 + .../css/crates/css-compiler/src/colors.rs | 54 + .../css/crates/css-compiler/src/conditions.rs | 778 +++ .../css/crates/css-compiler/src/expand.rs | 338 ++ .../css/crates/css-compiler/src/ffi.rs | 83 + .../css-compiler/src/known_properties.rs | 43 + .../css/crates/css-compiler/src/lib.rs | 706 +++ .../css/crates/css-compiler/tests/compiler.rs | 125 + .../crates/css-compiler/vendor/lexbor/LICENSE | 223 + .../vendor/lexbor/lexbor/core/array.c | 208 + .../vendor/lexbor/lexbor/core/array.h | 100 + .../vendor/lexbor/lexbor/core/array_obj.c | 216 + .../vendor/lexbor/lexbor/core/array_obj.h | 134 + .../vendor/lexbor/lexbor/core/avl.c | 525 ++ .../vendor/lexbor/lexbor/core/avl.h | 92 + .../vendor/lexbor/lexbor/core/base.h | 108 + .../vendor/lexbor/lexbor/core/bst.c | 471 ++ .../vendor/lexbor/lexbor/core/bst.h | 108 + .../vendor/lexbor/lexbor/core/bst_map.c | 238 + .../vendor/lexbor/lexbor/core/bst_map.h | 87 + .../vendor/lexbor/lexbor/core/conv.c | 356 ++ .../vendor/lexbor/lexbor/core/conv.h | 62 + .../vendor/lexbor/lexbor/core/core.h | 35 + .../vendor/lexbor/lexbor/core/def.h | 59 + .../vendor/lexbor/lexbor/core/diyfp.c | 181 + .../vendor/lexbor/lexbor/core/diyfp.h | 285 + .../vendor/lexbor/lexbor/core/dobject.c | 187 + .../vendor/lexbor/lexbor/core/dobject.h | 88 + .../vendor/lexbor/lexbor/core/dtoa.c | 401 ++ .../vendor/lexbor/lexbor/core/dtoa.h | 54 + .../vendor/lexbor/lexbor/core/fs.h | 60 + .../vendor/lexbor/lexbor/core/hash.c | 477 ++ .../vendor/lexbor/lexbor/core/hash.h | 218 + .../vendor/lexbor/lexbor/core/in.c | 266 + .../vendor/lexbor/lexbor/core/in.h | 172 + .../vendor/lexbor/lexbor/core/lexbor.h | 43 + .../vendor/lexbor/lexbor/core/mem.c | 228 + .../vendor/lexbor/lexbor/core/mem.h | 141 + .../vendor/lexbor/lexbor/core/mraw.c | 421 ++ .../vendor/lexbor/lexbor/core/mraw.h | 114 + .../vendor/lexbor/lexbor/core/perf.h | 40 + .../vendor/lexbor/lexbor/core/plog.c | 73 + .../vendor/lexbor/lexbor/core/plog.h | 102 + .../vendor/lexbor/lexbor/core/print.c | 168 + .../vendor/lexbor/lexbor/core/print.h | 39 + .../vendor/lexbor/lexbor/core/sbst.h | 59 + .../vendor/lexbor/lexbor/core/serialize.c | 27 + .../vendor/lexbor/lexbor/core/serialize.h | 32 + .../vendor/lexbor/lexbor/core/shs.c | 118 + .../vendor/lexbor/lexbor/core/shs.h | 82 + .../vendor/lexbor/lexbor/core/str.c | 667 ++ .../vendor/lexbor/lexbor/core/str.h | 257 + .../vendor/lexbor/lexbor/core/str_res.h | 360 ++ .../vendor/lexbor/lexbor/core/strtod.c | 348 ++ .../vendor/lexbor/lexbor/core/strtod.h | 54 + .../vendor/lexbor/lexbor/core/swar.h | 97 + .../vendor/lexbor/lexbor/core/types.h | 39 + .../vendor/lexbor/lexbor/core/utils.c | 43 + .../vendor/lexbor/lexbor/core/utils.h | 36 + .../vendor/lexbor/lexbor/css/at_rule.c | 449 ++ .../vendor/lexbor/lexbor/css/at_rule.h | 152 + .../vendor/lexbor/lexbor/css/at_rule/const.h | 32 + .../vendor/lexbor/lexbor/css/at_rule/res.h | 49 + .../vendor/lexbor/lexbor/css/at_rule/state.c | 374 ++ .../vendor/lexbor/lexbor/css/at_rule/state.h | 128 + .../vendor/lexbor/lexbor/css/at_rule/types.h | 60 + .../vendor/lexbor/lexbor/css/base.h | 136 + .../vendor/lexbor/lexbor/css/blank.c | 389 ++ .../vendor/lexbor/lexbor/css/blank.h | 38 + .../vendor/lexbor/lexbor/css/css.c | 254 + .../vendor/lexbor/lexbor/css/css.h | 52 + .../vendor/lexbor/lexbor/css/declaration.c | 86 + .../vendor/lexbor/lexbor/css/declaration.h | 33 + .../vendor/lexbor/lexbor/css/log.c | 168 + .../vendor/lexbor/lexbor/css/log.h | 103 + .../vendor/lexbor/lexbor/css/parser.c | 563 ++ .../vendor/lexbor/lexbor/css/parser.h | 756 +++ .../vendor/lexbor/lexbor/css/property.c | 3171 ++++++++++ .../vendor/lexbor/lexbor/css/property.h | 1754 ++++++ .../vendor/lexbor/lexbor/css/property/const.h | 1180 ++++ .../vendor/lexbor/lexbor/css/property/res.h | 524 ++ .../vendor/lexbor/lexbor/css/property/state.c | 5343 +++++++++++++++++ .../vendor/lexbor/lexbor/css/property/state.h | 330 + .../vendor/lexbor/lexbor/css/property/types.h | 21 + .../vendor/lexbor/lexbor/css/rule.c | 598 ++ .../vendor/lexbor/lexbor/css/rule.h | 453 ++ .../vendor/lexbor/lexbor/css/selectors/base.h | 47 + .../lexbor/lexbor/css/selectors/pseudo.c | 102 + .../lexbor/lexbor/css/selectors/pseudo.h | 70 + .../lexbor/css/selectors/pseudo_const.h | 110 + .../lexbor/lexbor/css/selectors/pseudo_res.h | 325 + .../lexbor/css/selectors/pseudo_state.c | 429 ++ .../lexbor/css/selectors/pseudo_state.h | 89 + .../lexbor/lexbor/css/selectors/selector.c | 1002 ++++ .../lexbor/lexbor/css/selectors/selector.h | 283 + .../lexbor/lexbor/css/selectors/selectors.c | 326 + .../lexbor/lexbor/css/selectors/selectors.h | 123 + .../lexbor/lexbor/css/selectors/state.c | 2060 +++++++ .../lexbor/lexbor/css/selectors/state.h | 86 + .../vendor/lexbor/lexbor/css/state.c | 676 +++ .../vendor/lexbor/lexbor/css/state.h | 59 + .../vendor/lexbor/lexbor/css/stylesheet.c | 111 + .../vendor/lexbor/lexbor/css/stylesheet.h | 90 + .../vendor/lexbor/lexbor/css/syntax/anb.c | 486 ++ .../vendor/lexbor/lexbor/css/syntax/anb.h | 46 + .../vendor/lexbor/lexbor/css/syntax/base.h | 33 + .../vendor/lexbor/lexbor/css/syntax/parser.c | 2251 +++++++ .../vendor/lexbor/lexbor/css/syntax/parser.h | 94 + .../vendor/lexbor/lexbor/css/syntax/res.h | 42 + .../vendor/lexbor/lexbor/css/syntax/state.c | 1852 ++++++ .../vendor/lexbor/lexbor/css/syntax/state.h | 128 + .../lexbor/lexbor/css/syntax/state_res.h | 273 + .../vendor/lexbor/lexbor/css/syntax/syntax.c | 537 ++ .../vendor/lexbor/lexbor/css/syntax/syntax.h | 255 + .../vendor/lexbor/lexbor/css/syntax/token.c | 664 ++ .../vendor/lexbor/lexbor/css/syntax/token.h | 333 + .../lexbor/lexbor/css/syntax/token_res.h | 82 + .../lexbor/lexbor/css/syntax/tokenizer.c | 397 ++ .../lexbor/lexbor/css/syntax/tokenizer.h | 123 + .../lexbor/css/syntax/tokenizer/error.c | 30 + .../lexbor/css/syntax/tokenizer/error.h | 60 + .../vendor/lexbor/lexbor/css/unit.c | 113 + .../vendor/lexbor/lexbor/css/unit.h | 46 + .../vendor/lexbor/lexbor/css/unit/const.h | 98 + .../vendor/lexbor/lexbor/css/unit/res.h | 294 + .../vendor/lexbor/lexbor/css/value.c | 678 +++ .../vendor/lexbor/lexbor/css/value.h | 290 + .../vendor/lexbor/lexbor/css/value/const.h | 397 ++ .../vendor/lexbor/lexbor/css/value/res.h | 775 +++ .../lexbor/ports/posix/lexbor/core/memory.c | 53 + .../css-compiler/vendor/stubs/inttypes.h | 4 + .../crates/css-compiler/vendor/stubs/math.h | 12 + .../crates/css-compiler/vendor/stubs/memory.h | 4 + .../css-compiler/vendor/stubs/stdbool.h | 6 + .../crates/css-compiler/vendor/stubs/stdio.h | 5 + .../crates/css-compiler/vendor/stubs/stdlib.h | 9 + .../crates/css-compiler/vendor/stubs/string.h | 14 + .../css/crates/css-conformance/Cargo.toml | 14 + .../cases/cascade/01-layers.css | 5 + .../cases/cascade/02-important.css | 3 + .../cases/colors/01-modern.css | 12 + .../css-conformance/cases/colors/02-hex.css | 12 + .../cases/conditions/01-media.css | 6 + .../cases/conditions/02-container.css | 7 + .../cases/conditions/03-supports.css | 5 + .../cases/nesting/01-basic.css | 13 + .../cases/nesting/02-deep-nesting.css | 10 + .../cases/nesting/03-nested-conditions.css | 12 + .../cases/nesting/04-nest-at-rule.css | 8 + .../cases/selectors/01-level4.css | 8 + .../cases/selectors/02-attributes.css | 7 + .../cases/selectors/03-combinators.css | 4 + .../css-conformance/cases/values/01-var.css | 5 + .../cases/values/02-single-line.css | 7 + .../crates/css-conformance/tests/behavior.rs | 360 ++ .../css-conformance/tests/conformance.rs | 215 + experiments/css/crates/css-core/Cargo.toml | 18 + experiments/css/crates/css-core/src/lib.rs | 458 ++ .../css/crates/css-core/src/selectors.rs | 526 ++ experiments/css/crates/css-core/src/tokens.rs | 172 + experiments/css/crates/css-runtime/Cargo.toml | 17 + .../css/crates/css-runtime/src/cascade.rs | 529 ++ .../css/crates/css-runtime/src/conditions.rs | 415 ++ .../css/crates/css-runtime/src/element.rs | 414 ++ experiments/css/crates/css-runtime/src/lib.rs | 38 + .../css/crates/css-runtime/src/selectors.rs | 40 + .../css/crates/css-runtime/src/values.rs | 233 + .../css/crates/css-runtime/tests/runtime.rs | 567 ++ experiments/css/crates/css-wasm/Cargo.toml | 17 + experiments/css/crates/css-wasm/src/lib.rs | 232 + experiments/css/demo/README.md | 35 + experiments/css/demo/abi.js | 478 ++ experiments/css/demo/bench.mjs | 145 + experiments/css/demo/build.sh | 34 + experiments/css/demo/index.html | 61 + experiments/css/demo/main.js | 89 + experiments/css/demo/smoke.mjs | 140 + experiments/css/demo/styles.css | 109 + experiments/css/tasks/bundle-wasm.ts | 63 + experiments/css/tasks/verify-bundle.ts | 85 + experiments/css/wit/css.wit | 125 + 193 files changed, 53017 insertions(+) create mode 100644 experiments/css/.gitignore create mode 100644 experiments/css/BUGS-from-ui-bridge.md create mode 100644 experiments/css/Cargo.lock create mode 100644 experiments/css/Cargo.toml create mode 100644 experiments/css/HANDOFF-compute-poison.md create mode 100644 experiments/css/README.md create mode 100644 experiments/css/crates/css-compiler/Cargo.toml create mode 100644 experiments/css/crates/css-compiler/build.rs create mode 100644 experiments/css/crates/css-compiler/c/shim.c create mode 100644 experiments/css/crates/css-compiler/examples/compile_time.rs create mode 100644 experiments/css/crates/css-compiler/examples/events.rs create mode 100644 experiments/css/crates/css-compiler/src/alloc_shim.rs create mode 100644 experiments/css/crates/css-compiler/src/canonical.rs create mode 100644 experiments/css/crates/css-compiler/src/colors.rs create mode 100644 experiments/css/crates/css-compiler/src/conditions.rs create mode 100644 experiments/css/crates/css-compiler/src/expand.rs create mode 100644 experiments/css/crates/css-compiler/src/ffi.rs create mode 100644 experiments/css/crates/css-compiler/src/known_properties.rs create mode 100644 experiments/css/crates/css-compiler/src/lib.rs create mode 100644 experiments/css/crates/css-compiler/tests/compiler.rs create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/LICENSE create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/base.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/core.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/def.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/fs.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/lexbor.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/perf.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/sbst.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str_res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/swar.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/types.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/const.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/types.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/base.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/const.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/types.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/base.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_const.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/base.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state_res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token_res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/const.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.c create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/const.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/res.h create mode 100644 experiments/css/crates/css-compiler/vendor/lexbor/lexbor/ports/posix/lexbor/core/memory.c create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/inttypes.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/math.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/memory.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/stdbool.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/stdio.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/stdlib.h create mode 100644 experiments/css/crates/css-compiler/vendor/stubs/string.h create mode 100644 experiments/css/crates/css-conformance/Cargo.toml create mode 100644 experiments/css/crates/css-conformance/cases/cascade/01-layers.css create mode 100644 experiments/css/crates/css-conformance/cases/cascade/02-important.css create mode 100644 experiments/css/crates/css-conformance/cases/colors/01-modern.css create mode 100644 experiments/css/crates/css-conformance/cases/colors/02-hex.css create mode 100644 experiments/css/crates/css-conformance/cases/conditions/01-media.css create mode 100644 experiments/css/crates/css-conformance/cases/conditions/02-container.css create mode 100644 experiments/css/crates/css-conformance/cases/conditions/03-supports.css create mode 100644 experiments/css/crates/css-conformance/cases/nesting/01-basic.css create mode 100644 experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css create mode 100644 experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css create mode 100644 experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css create mode 100644 experiments/css/crates/css-conformance/cases/selectors/01-level4.css create mode 100644 experiments/css/crates/css-conformance/cases/selectors/02-attributes.css create mode 100644 experiments/css/crates/css-conformance/cases/selectors/03-combinators.css create mode 100644 experiments/css/crates/css-conformance/cases/values/01-var.css create mode 100644 experiments/css/crates/css-conformance/cases/values/02-single-line.css create mode 100644 experiments/css/crates/css-conformance/tests/behavior.rs create mode 100644 experiments/css/crates/css-conformance/tests/conformance.rs create mode 100644 experiments/css/crates/css-core/Cargo.toml create mode 100644 experiments/css/crates/css-core/src/lib.rs create mode 100644 experiments/css/crates/css-core/src/selectors.rs create mode 100644 experiments/css/crates/css-core/src/tokens.rs create mode 100644 experiments/css/crates/css-runtime/Cargo.toml create mode 100644 experiments/css/crates/css-runtime/src/cascade.rs create mode 100644 experiments/css/crates/css-runtime/src/conditions.rs create mode 100644 experiments/css/crates/css-runtime/src/element.rs create mode 100644 experiments/css/crates/css-runtime/src/lib.rs create mode 100644 experiments/css/crates/css-runtime/src/selectors.rs create mode 100644 experiments/css/crates/css-runtime/src/values.rs create mode 100644 experiments/css/crates/css-runtime/tests/runtime.rs create mode 100644 experiments/css/crates/css-wasm/Cargo.toml create mode 100644 experiments/css/crates/css-wasm/src/lib.rs create mode 100644 experiments/css/demo/README.md create mode 100644 experiments/css/demo/abi.js create mode 100644 experiments/css/demo/bench.mjs create mode 100755 experiments/css/demo/build.sh create mode 100644 experiments/css/demo/index.html create mode 100644 experiments/css/demo/main.js create mode 100644 experiments/css/demo/smoke.mjs create mode 100644 experiments/css/demo/styles.css create mode 100644 experiments/css/tasks/bundle-wasm.ts create mode 100644 experiments/css/tasks/verify-bundle.ts create mode 100644 experiments/css/wit/css.wit diff --git a/experiments/css/.gitignore b/experiments/css/.gitignore new file mode 100644 index 0000000..32541a4 --- /dev/null +++ b/experiments/css/.gitignore @@ -0,0 +1,3 @@ +/target/ +/demo/bombshell_css_wasm.wasm +/wasm.ts diff --git a/experiments/css/BUGS-from-ui-bridge.md b/experiments/css/BUGS-from-ui-bridge.md new file mode 100644 index 0000000..f3eb375 --- /dev/null +++ b/experiments/css/BUGS-from-ui-bridge.md @@ -0,0 +1,154 @@ +# Bug report: compile/compute regressions hit from the UI DOM bridge + +_From the bombshell-dev/ui spike (DOM core + CSS bridge). Written 2026-10-12. Reproed against `demo/bombshell_css_wasm.wasm` (214,025 bytes, md5 `e2848c97d90ece5153e05a096ada3a2d`) + unmodified `demo/abi.js` — the exact artifact/ABI the smoke test passes with (`node demo/smoke.mjs` → all green, 5500 computes, 0 memory growth)._ + +Two independent failures, both silently (empty `diagnostics`): + +## Bug 3 — `compute_tree` poisons subsequent `compile` calls (the big one) + +Minimal sequence, fresh process: + +```js +const ua = component.compile('tui-button { display: flex; border: 1px solid #3a3a46; cursor: pointer }', 0); +const uaSheet = ua.ir && component.prepare(ua.ir); +component.computeTree(uaSheet, nodes, parents, env); // one compute is enough +const author = component.compile('tui-text { color: red }', 2); +console.log(author.ir); // → null, diagnostics: [] +``` + +Controls (same component code paths, minus one step each): + +| sequence | second compile | +| --- | --- | +| compile → prepare → **compute_tree** → compile | **ir: null** (silent) | +| compile → prepare → compile | ir: 34 ✓ | +| compile → compile (no prepare) | ir: 34 ✓ | + +So one `compute_tree` call breaks every later `compile` — with empty diagnostics. This is why the smoke suite can't see it: it compiles once, computes many. The UI bridge compiles at runtime (hot adoption) and hits it constantly. Host-side workaround in place (fresh component re-instantiation per compile batch, `createCssComponentSync`), but the real fix belongs here — my guess is allocator/arena state (the compute-tree's lifted-list frees) that `compile`'s next call walks into, possibly related to the recent table-packing commits. + +Repro: the three-line sequence above against `demo/bombshell_css_wasm.wasm` (md5 `858bcb0c23f8881a8c87254a23418b6a`). + +## Bug 1 — single-line stylesheet compiles to NO IR + +```js +component.compile('.card { color: #fff; padding: 4px }', 2) // → { ir: null, diagnostics: [] } +component.compile('.card {\n color: #fff;\n padding: 4px;\n}', 2) // → { ir: <74 bytes>, diagnostics: [] } +``` + +Identical CSS modulo whitespace/newlines. The single-line form produces **no IR and no diagnostics** — `prepare` then rejects (`null`), and the host can't tell the user why. Looks like a token-level event from the lexbor shim being lost when the rule body ends on the same line it starts (the smoke's stylesheet — like all real stylesheets — is multiline, which is why the corpus doesn't catch it). Suggests a conformance-corpus gap: add single-line-rule cases to `css-conformance/cases/`. + +## Bug 2 — hex color declarations: dropped from computed, or NO IR, depending on digit count + +Matrix (each row a fresh process, multiline sheet `.card {\n ;\n}`, compute-tree on one `.card` node): + +| declaration | compile | computed | +| --- | --- | --- | +| `color: red` | ✓ | `color: red` present ✓ | +| `color: rgb(40 40 48)` | ✓ | present ✓ | +| `color: light-dark(white, black)` (smoke) | ✓ | `color: #fff` present ✓ | +| `background-color: #101010` | ✓ | present ✓ | +| `border-color: rgb(40 40 48)` | ✓ | present ✓ | +| `color: #fff` | ✓ | **declaration vanishes** (computed `[[]]`) | +| `color: #ff8800` | ✓ | **declaration vanishes** | +| `color: #ffffff` | **NO IR**, no diagnostics | — | +| `border-color: #333` | **NO IR**, no diagnostics | — | +| `border-color: #333333` | **NO IR**, no diagnostics | — | + +So: named/functional colors are fine; a 3-digit hex on `color` compiles but the declaration is silently dropped downstream; 6-digit hex (and 3-digit hex on `border-color`) silently produce no IR at all. `background-color: #101010` (6-digit hex) surviving while `color: #ffffff` dies suggests the canonicalizer/path differs per property — likely a span/boundary issue around the `#` hex token in the lexbor shim's event stream for the `color`/`border-color` grammar specifically. + +Guess at the culprit: the compiler's canonical value serialization for hex colors, or the runtime's declaration filter not recognizing the canonical form for those properties. Whatever it is, `diagnostics: []` on all the failures is its own bug — silent data loss. + +Also observed (lower confidence, only if useful): running several `compile()` calls back-to-back in one process, the later ones occasionally produced `ir: null` as well (seen with single-line inputs) — could be entirely explained by Bug 1; flagging in case there's per-call state involved. + +## Context: why the UI bridge cares + +The ui DOM bridge feeds `compute-tree` a flat element snapshot per frame and merges the returned declarations onto tty render properties. `color`/`border-color` are *the* properties for terminal text and borders — Bug 2 makes stylesheet-driven terminal styling impossible for plain colors, and Bug 1 makes half of real-world CSS (minified/single-line sheets) silently no-op. + +Repro script (run from `demo/`): the two snippets above; `demo/smoke.mjs` continues to pass, so any fix should be asserted by extending the corpus with: (a) single-line rules, (b) plain-hex color + border-color computed expectations. + +— the ui-side spike + +--- + +## Resolution (engine side, 2026-10-12) + +All three bugs are fixed; the "engine poison" in Bug 3 was never engine state. + +**Bug 3 (`compute_tree` "poisons" later `compile`/`prepare`): host-side ABI misread.** +The canonical ABI lowers `option<...>` results as a 1-byte discriminant followed +by 3 bytes of *undefined* padding (then the payload at +4). wit-bindgen's guest +shares one static return area across all exports, and `compute_tree` writes an +outer-list pointer there; a later `compile`/`prepare` writes its discriminant +over byte 0 and leaves the stale pointer fragments in bytes 1..3. The old host +readers (`demo/abi.js`, and the ui's `abi.ts` adapted from it) read the +discriminant as `u32` — so a fully successful guest result (`disc=1, handle=1`) +read as `0x00110201 ≠ 1` → "null", with the guest's real diagnostics never +examined. That is the entire poison matrix: fresh components work (zeroed +return area), `compute` is immune (no discriminant in its result), and the +same IR bytes work on a fresh component. Byte-level trace: after one +`compute_tree` whose outer list lives at `0x00110250`, the next `compile` +returns `01 02 11 00 …` — disc `1`, padding `02 11 00`, valid IR pointer and +length behind it. + +Fixes: `demo/abi.js` and the ui's `packages/ui/src/css/abi.ts` now read the +discriminant with `getUint8`; the ui bridge's fresh-component-reload +workaround is deleted. The engine's wasm guest is unchanged — the rebuilt +artifact is byte-identical (214,412 bytes, md5 `858bcb0c23f8881a8c87254a23418b6a`). +Regression protection: `demo/smoke.mjs` asserts the full matrix +(compile/prepare/re-prepare after compute-tree, plus a sheet adopted and +computed after the compute window), and `css-conformance` pins the +library-level property (`compile_and_prepare_survive_compute_tree`). + +Known limitations left as-is (WIT `@0.3.0` shapes, not regressions): +`prepare` has no diagnostics channel (invalid IR → `null` only), and +`compute`/`compute_tree` degrade to empty results for unknown handles — +handles are never invalidated on `release`, and a freed slot can be reused by +a later `prepare`, so a stale handle aliases the new sheet. Worth a `@0.4.0` +discussion someday; nothing here depends on it. + +**Bug 1 (single-line stylesheets produce no IR): fixed in the compiler.** +The lexbor shim's token stream lost the rule-body-end event when the closing +brace sat on the rule's first line. Corpus case `values/02-single-line.css` + +behavior test. + +**Bug 2 (hex colors dropped or NO IR): fixed in the compiler.** +Hex colors tokenize as `Hash` or `IDHash` depending on whether the payload +reads as an identifier; only `Hash` was recognized, and digit-count +validation was missing entirely. Both token forms are now accepted and +validated as 3/4/6/8 hex digits. Corpus case `colors/02-hex.css` + behavior +test; the smoke asserts hex reaches computed styles. The ui bridge test NOTE +about avoiding hex in fixtures is updated — hex stylesheet fixtures are safe +on the current artifact (md5 `858bcb0c…`). + +Diagnostics are no longer silent for parse failures: recovery warnings ride +the compile output (and the IR), and hard errors preserve the warnings +observed before them. + +## Bug 4 (new, perf) — the recovery path is super-linear in the number of invalid rules + +Found while benchmarking adopt/recompile cycles from the ui side (2026-10-12, +same artifact `858bcb0c…`). A stylesheet whose selectors are invalid (e.g. +`.probe-1-0.5123…` — a class token starting with a digit) triggers parser +recovery **per rule**, and compile cost grows super-linearly with the count of +recovered rules. Measured on one component, growing join, 40 rules of +`~45 bytes` each: + +| shape | compile at rule 1 | at rule 20 | at rule 40 | +| --- | --- | --- | --- | +| valid, unique class names | 0.13 ms | ~0.18 ms | ~0.26 ms (linear ✓) | +| invalid idents (recovery per rule) | 0.19 ms | ~12 ms | ~21–37 ms | + +diagnostics are correct in both shapes (1:1 with the invalid rules); linear +memory stays flat (22–24 pages) — so this is the recovery walk itself +(presumably re-scanning accumulated state per recovery), not a leak. Valid +input never enters this path, which is why the corpus/smoke don't see it. + +Repro: compile `n` rules of `.probe-${i}- { color: … }` joined +into one sheet on a single component; compare against the same shape with +valid unique class names. Suggested corpus case: an `n`-rule recovery sheet +with a compile-time budget; suggested fix direction: whatever state the +recovery walk re-traverses per rule (candidate: the shared writer/table state +the packed-tables commits touched) should reset per compile. + +Impact: real-world legacy/generated stylesheets with many invalid rules pay +this silently. The ui never does on valid CSS (its recompiles stay ~0.2 ms). diff --git a/experiments/css/Cargo.lock b/experiments/css/Cargo.lock new file mode 100644 index 0000000..30280be --- /dev/null +++ b/experiments/css/Cargo.lock @@ -0,0 +1,623 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" + +[[package]] +name = "bitflags" +version = "2.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" + +[[package]] +name = "bombshell-css-compiler" +version = "0.1.0" +dependencies = [ + "bombshell-css-core", + "cc", + "cssparser", +] + +[[package]] +name = "bombshell-css-conformance" +version = "0.1.0" +dependencies = [ + "bombshell-css-compiler", + "bombshell-css-core", + "bombshell-css-runtime", +] + +[[package]] +name = "bombshell-css-core" +version = "0.1.0" +dependencies = [ + "cssparser", + "parcel_selectors", + "postcard", + "serde", +] + +[[package]] +name = "bombshell-css-runtime" +version = "0.1.0" +dependencies = [ + "bombshell-css-compiler", + "bombshell-css-core", + "cssparser", + "parcel_selectors", + "rustc-hash", +] + +[[package]] +name = "bombshell-css-wasm" +version = "0.1.0" +dependencies = [ + "bombshell-css-compiler", + "bombshell-css-core", + "bombshell-css-runtime", + "wit-bindgen", +] + +[[package]] +name = "cc" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cobs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1" +dependencies = [ + "thiserror", +] + +[[package]] +name = "cssparser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf 0.13.1", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a" +dependencies = [ + "quote", + "syn 3.0.6", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "embedded-io" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced" + +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fastrand" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" + +[[package]] +name = "find-msvc-tools" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "log" +version = "0.4.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "parcel_selectors" +version = "0.28.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05f71e01edca03d245ab0a9f7ce13a974ceb79baaae8faf2ba0b11de6b90913" +dependencies = [ + "bitflags", + "cssparser", + "log", + "phf 0.11.3", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "smallvec", +] + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared 0.13.1", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +dependencies = [ + "phf_generator 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared 0.13.1", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "postcard" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" +dependencies = [ + "cobs", + "embedded-io 0.4.0", + "embedded-io 0.6.1", + "serde", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bfe0f4c752e450fc2faf62654f1c134747922825d5b04ca717b8874f41a40c0" +dependencies = [ + "proc-macro2", + "syn 3.0.6", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "serde_json" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "siphasher" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33f4fe9184a62d842c9ef383018f3306d8ba224fd9d836f56d7288308847c256" + +[[package]] +name = "smallvec" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba467056f1b547ed52077911161fc86985becbc60e8e1857c8a144dab0def891" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", +] + +[[package]] +name = "unicode-ident" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab72a15cf68d77cb0987d3684aa8a45c5ef827e8cb49ee2f30bfd7ba2feb519f" + +[[package]] +name = "wasm-encoder" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d0246511d901aacf25d2dc9111f0054947de5e093fe662099e709ff7530dc3" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7bf119eb01f4a246864c10bd87c8f26c566350abb25aa4d5273c415bbab338" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f7c12eac7bb587801590f6a67ff0bc84d0748513864b71108e4b311cc3df694" +dependencies = [ + "bitflags", + "hashbrown", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53cb4b5556c3a791e86838ea287782bdafa704d55b0e68b5b81a3a16b9ea5f4b" +dependencies = [ + "bitflags", + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ad22a37ecbc0e1fdca34d2b670ba41368cb0920735643e00fdf39a16ee5431" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84ba5213d4332c260a0d4e69e34b0f7e4b85ddae10b6ebfbac00d9c185ba628a" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 3.0.6", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f19d63da8e478a370ef8192d90ccb517965b7747d6b46cd2d831dd0ef4d2541" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 3.0.6", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092f783dee3253265fcde131475055f8c275ac0176a9c0396db1dc7b5544bfb4" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.259.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6390f4f02493ce678d509c859cf1698e38929fb75c8012a6ee2f7d6b6cb66cd7" +dependencies = [ + "anyhow", + "hashbrown", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-ident", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/experiments/css/Cargo.toml b/experiments/css/Cargo.toml new file mode 100644 index 0000000..fdb6cb8 --- /dev/null +++ b/experiments/css/Cargo.toml @@ -0,0 +1,34 @@ +[workspace] +members = [ + "crates/css-core", + "crates/css-compiler", + "crates/css-runtime", + "crates/css-wasm", + "crates/css-conformance", +] +resolver = "2" + +[workspace.package] +edition = "2024" +license = "MIT" +repository = "https://github.com/natemoo-re/css" +rust-version = "1.85" + +[workspace.dependencies] +bombshell-css-core = { path = "crates/css-core" } +bombshell-css-compiler = { path = "crates/css-compiler" } +bombshell-css-runtime = { path = "crates/css-runtime" } +cc = "1" +cssparser = "0.37.0" +parcel_selectors = "0.28.3" +rustc-hash = "2" +serde = { version = "1", default-features = false, features = ["derive", "alloc"] } +postcard = { version = "1", default-features = false, features = ["alloc"] } +wit-bindgen = { version = "0.62.0", default-features = false, features = ["macros", "realloc", "bitflags"] } + +[profile.release] +lto = true +codegen-units = 1 +panic = "abort" +opt-level = "z" +strip = true diff --git a/experiments/css/HANDOFF-compute-poison.md b/experiments/css/HANDOFF-compute-poison.md new file mode 100644 index 0000000..c9946c0 --- /dev/null +++ b/experiments/css/HANDOFF-compute-poison.md @@ -0,0 +1,86 @@ +# Handoff: `compute_tree` poisons later `compile` **and** `prepare` calls + +For an agent picking up the Bombshell CSS engine (`natemoo-re/css`). Written 2026-10-12 from the consuming side (`bombshell-dev/ui`, package `@clack/ui`). Companion doc: `BUGS-from-ui-bridge.md` (same repo) — read it first; this extends it with new findings and the fix brief. + +## The one-paragraph brief + +One `compute_tree` call puts the whole wasm component into a state where every later `compile` **and** `prepare` silently returns null/empty (empty `diagnostics` — the silence is part of the bug). Sheets that already exist keep computing forever, so the per-frame path is fine; what breaks is any stylesheet adoption after the first frame. The UI bridge ships a host-side workaround (fresh component re-instantiation per compile batch) that this fix should make deletable. Fix the engine, prove it in the conformance corpus + smoke, then the UI deletes the workaround. + +## Repos, versions, artifacts + +- Engine: `/Users/nmoo/Developer/natemoo-re/css` — Rust workspace, crates: `css-core` (IR encode/decode, types), `css-compiler` (source → IR, lexbor front-end), `css-runtime` (prepare/cascade/compute), `css-wasm` (the WIT component adapter), `css-conformance` (corpus tests). WIT contract: `wit/css.wit`, interface `bombshell:css/stylesheet@0.3.0`. +- Consumer: `/Users/nmoo/Developer/bombshell-dev/ui`, package `@clack/ui` — vendored the built artifact at `packages/ui/src/css/bombshell-css.wasm`, ABI bindings in `packages/ui/src/css/abi.ts` (adapted from `demo/abi.js`). +- Artifact state: `demo/bombshell_css_wasm.wasm` == `packages/ui/dist/bombshell-css.wasm` == `packages/ui/src/css/bombshell-css.wasm` (md5 `858bcb0c23f8881a8c87254a23418b6a`, 214,412 bytes). Any rebuild should be copied into the ui package (both `src/css/` and `dist/`; the ui build does `cp -f src/css/bombshell-css.wasm dist/`). + +## The full poison matrix (measured 2026-10-12, fresh process each row) + +| sequence | result | +| --- | --- | +| compile → prepare → compute-tree ×N (same sheets) | ✓ stable — 100+ computes, identical output | +| compile → prepare → compute-tree → **compile** | `ir: null`, `diagnostics: []` (known, Bug 3) | +| compile → prepare → compute-tree → **prepare(existing IR bytes)** | **`prepare → null` — NEW** | +| fresh component → `prepare` (the same IR bytes that failed above) | ✓ works — the IR is fine, the component state is not | +| fresh component → `compile` | ✓ works | +| compute-tree with a sheet prepared *after* the poison | returns **0 node results** (empty per-node lists) | + +Key new facts: + +1. **`prepare` joins `compile` in the poisoned set.** It isn't a compiler-side arena; whatever `compute_tree` leaves behind breaks *both* entry points that allocate at call start. +2. **`compute` itself keeps working after the poison** (alloc/free cycles per call succeed — the smoke's "compiles once, computes many" is why CI never saw this). So the global allocator is not broadly corrupted; the failure is specific to what `compile`/`prepare` do first — both go through the compiler/decode front doors that `compute_tree` never re-enters. +3. Failures are **silent**: `diagnostics: []` on every path. Whatever the fix is, a non-recoverable internal state should at minimum emit a diagnostic (or panic — this is a guest module; a loud abort beats silent data loss). + +## Suspect area and leads + +- Adapter: `crates/css-wasm/src/lib.rs` (232 lines — read it whole). + - `prepare` decodes IR and `Box::leak`s it into a `SHEETS` slot (`thread_local!` `RefCell>>`); `release` reconstructs + frees. Decode allocates through the global allocator. + - `compile` → `bombshell_css_compiler::compile(&css, origin, CompileOptions::default())` + `encode_ir`. + - `compute_tree` → `bombshell_css_runtime::cascade::compute_tree`, then the canonical-ABI lift of `list>` — wit-bindgen **frees the lifted lists via `cabi_post_*` hooks**. The bug doc's theory ("allocator/arena state, the compute-tree's lifted-list frees, that compile's next call walks into") is consistent with prepare also dying: both begin by allocating from whatever state the lifts/frees disturbed. +- Recent commits on the exact machinery (suspect range): + - `a8203e6` — "Serialize CSS tokens through one shared writer" + - `a7eba99` — "Pack the color and known-property tables into blobs" + - `eddd906` — "Drop dead Ordering imports from the packed tables" (HEAD) + - Also relevant context: `c0d9fe6` "Make the runtime a per-frame engine: rule index, borrowed cascade, tree API" — the per-frame rework that compute_tree belongs to. +- Things worth instrumenting first: the first allocation each of `compile`/`prepare` performs after a `compute_tree`; the `cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree` free path; whether the blob-packed tables are built in scratch memory that `compute_tree`'s frees disturb; whether `SHEETS` slot bookkeeping (the `slots.push(None)` growth in `prepare`) collides with anything compute_tree wrote. + +## How to reproduce + +`demo/` has the reference host harness (`abi.js`, `smoke.mjs`). Minimal repro (fresh process): + +```js +const component = await loadCssComponent(readFileSync('./demo/bombshell_css_wasm.wasm')); +const ua = component.compile('div { color: red; padding: 1px }', 0); +const sheet = component.prepare(ua.ir); // works +component.computeTree(sheet, nodes, parents, env); // one call is enough +console.log(component.compile('div { color: red }', 2)); // → ir: null, diagnostics: [] +console.log(component.prepare(ua.ir)); // → null (NEW) +``` + +`nodes`/`parents`/`env` shapes are in `demo/abi.js` (`CssNode` 56-byte stride encode; `parents = [NO_PARENT, 0, …]`; env = `{ viewportWidth, viewportHeight, colorScheme: 'dark', reducedMotion, resolution }`). + +Expected after the fix: both calls return live values; the poisoned-handle compute path (sheet handle reused post-release) either works or errors loudly. + +## Definition of done + +1. Engine: a unit/conformance test reproducing the matrix above goes green. Extend `css-conformance` and `demo/smoke.mjs` with the compile→compute→compile/prepare-again sequence so the corpus can't regress. The repo already has uncommitted corpus additions for Bugs 1/2 (`crates/css-conformance/cases/colors/02-hex.css`, `cases/values/02-single-line.css`) — keep them. +2. Loud failure mode: internal-bail paths surface a diagnostic (or guest abort), never a silent empty return. +3. No measurable perf/size regression on the hot path: per-frame `compute_tree` is ~0.02 ms for a 3-node tree today (0.020 ms measured, avg of 1000); keep it within noise. Artifact is size-golfed (nightly `-Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none` + double `wasm-opt -Oz`, see `demo/build.sh`) — report the new byte count; ui copies it verbatim. +4. Consumer-side cleanup (may be a separate step): `bombshell-dev/ui` `packages/ui/src/css/bridge.ts` deletes the workaround in `#recompile` (the `createCssComponentSync` fresh-instantiation per compile batch, currently ~lines 168–193) and prepares new sheets on the long-lived component; `packages/ui/src/css/bridge.test.ts` (16 files / 213 tests, all green at time of writing) plus the headless E2E (`examples/dom-demo`: `node --experimental-strip-types src/headless.ts`) must stay green. + +## Perf baseline (for the comparison after your fix) + +| operation | today | +| --- | --- | +| wasm instantiate (full reload) | 0.151 ms | +| compile, 1.2 KB sheet (UA) | 0.070 ms | +| compile, 740 B sheet | 0.034 ms | +| compile, 60 KB / 500 rules | 4.85 ms | +| prepare, same three sheets | 0.024 / 0.010 / 1.71 ms | +| compute-tree, 3 nodes | 0.020 ms | + +(Precompiling CSS to IR at build time is *not* a substitute for this fix — prepare is poisoned too; the poison fix is the real one. IR portability across instances is confirmed, so a future build-time `.css.ir` artifact path is viable once this lands.) + +## Working agreements + +- The repo has **uncommitted work** (canonical.rs, lib.rs, behavior.rs, demo/abi.js, smoke.mjs + two corpus cases) — it is the in-flight Bugs 1/2 fix; build on it, don't revert it. +- IR/ABI changes require bumping the WIT version (`@0.3.0`) only if the IR format itself changes; a pure state-fix shouldn't. +- Node 22+/24 host, wasm target `wasm32-unknown-unknown`, nightly toolchain required by `demo/build.sh`. diff --git a/experiments/css/README.md b/experiments/css/README.md new file mode 100644 index 0000000..cf398b6 --- /dev/null +++ b/experiments/css/README.md @@ -0,0 +1,206 @@ +# Bombshell CSS + +The CSS pipeline for [Bombshell UI](https://github.com/bombshell-dev/ui): a +full modern-CSS compiler front-end, a selector/cascade runtime, and a +freestanding wasm component that speaks WIT. + +## Architecture + +``` +CSS text ──▶ bombshell-css-compiler ──▶ typed IR (v2, postcard bytes) + (lexbor C shim: token-level events, + Rust semantics: nesting expansion, + condition parsing, canonicalization) + │ + ▼ + bombshell-css-runtime ──▶ computed declarations + (parcel_selectors matcher, + cascade + inheritance, + custom properties / var(), + media/container/supports eval) + │ + ▼ + bombshell-css-wasm (WIT world `bombshell:css@0.3.0`) +``` + +- **`bombshell-css-core`** — shared contracts: the versioned compiled- + stylesheet IR, the flat DOM snapshot (`TreeSnapshot`), the render + `Environment`, the wire codec, and (behind the default `selectors` + feature) the `parcel_selectors` selector engine that both the compiler + and the runtime drive. The IR half stays `serde` + `postcard` and + `no_std`-capable. +- **`bombshell-css-compiler`** — the front-end. A vendored + [lexbor](https://github.com/lexbor/lexbor) (Apache-2.0) drives a C shim + (`c/shim.c`) that runs lexbor's syntax parser with custom callbacks and + emits a flat event stream of byte spans. Everything semantic happens in + Rust: nesting expansion per the CSS Nesting spec (`&` → parent + substitution), typed condition trees parsed from raw preludes + (`@media`/`@container`/`@supports`), selector validation and specificity + through the shared `parcel_selectors` parser (so the compiler and the + runtime matcher can never disagree about what is valid), and canonical + value serialization (numbers, colors, `light-dark()`, `font-weight` + keywords). Selectors and declaration values cross the boundary as + canonical CSS text; conditions cross as typed trees. +- **`bombshell-css-runtime`** — the engine. Selector matching runs on + `parcel_selectors` (the selector engine lightningcss itself uses) adapted + to the snapshot model. The cascade implements origin/importance, `@layer` + ordering, specificity, and source order; inheritance, custom properties, + `var()` substitution (with fallbacks and cycle detection), and CSS-wide + keywords (`inherit`, `unset`, `initial`, `revert`) resolve per element. +- **`bombshell-css-wasm`** — the WIT adapter. `compile(css, origin)` returns + opaque IR bytes plus diagnostics; `prepare(ir)` decodes and parses a + stylesheet once and returns a handle; `compute(handle, …)` styles one node + and `compute-tree(handle, nodes, parents, env)` styles the whole flat tree + in a single pass — the per-frame entry point, with each node's cascade + running exactly once per frame. `release(handle)` drops a prepared sheet. + The host's linear-memory contract stays free of Rust/JS object layouts, + and the module is freestanding — zero imports (the C side gets a tiny + Rust-backed `malloc` family). +- **`bombshell-css-conformance`** — the corpus: `cases/` fixtures with a + compile/round-trip runner plus behavior tests that compile cases and + assert computed results. + +## Size and speed + +The lexbor front-end replaced lightningcss (MPL-2.0) to cut module weight — +lightningcss was ~94% of the artifact. The size pass on top (vendored-lexbor +trimming, `opt-level = "z"`, nightly `immediate-abort` panics, and the +selector-parsing unification below) roughly halved it again: + +| | before (lightningcss) | after (lexbor) | size + speed pass | +| --- | --- | --- | --- | +| full component (wasm-opt -Oz) | 3.31 MB / 780 KB gzip | 471 KB / 177 KB gzip | **214 KB / 87 KB gzip** | +| compile throughput (77 KB corpus, wasm) | ~15.7 MB/s | ~11.4 MB/s | ~15.1 MB/s | +| whole-tree compute (18-node tree, 1151 rules) | — | ~194 µs/node | **~13 µs/node** | + +`node demo/bench.mjs` re-measures the shipped artifact. + +## The tree ABI + +`compute` receives the whole subtree as a flat, document-ordered node list +plus a parent-index vector (`u32::MAX` = root). This recursion-free shape is +WIT-friendly yet complete: structural pseudo-classes (`:nth-child`, +`:first/last/only-child`), sibling combinators (`+`, `~`), and `:has()` +traversal all work off the snapshot. Ancestors are cascaded automatically so +inheritance, custom properties, and container lookups (`container-type`, +`container-name` from computed styles; sizes from node layout) compose. + +`compute-tree` is the per-frame entry point: one call styles every node, +each node's cascade runs exactly once, and descendants inherit computed +values (var() substitution happens per level, so inheritance carries +computed — not raw — values). The frame loop allocates nothing that +outlives the call; `demo/smoke.mjs` asserts hundreds of full-tree frames +grow memory by zero bytes. Loaded stylesheets borrow the parsed selectors +from the IR — `Stylesheet::from_ir(&ir, …)` is allocation-free apart from +parsing — and a rule index built from the parsed selectors' rightmost +compounds keeps per-node matching proportional to relevant rules, not to +the whole sheet. + +## Build + +```sh +cargo test --workspace + +# The shipped artifact needs nightly (std is rebuilt with immediate-abort +# panics and panic-location strings disabled) plus Binaryen: +./demo/build.sh +``` + +`demo/build.sh` runs `cargo +nightly build -Z build-std=std,panic_abort` with +`-Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none` and then +`wasm-opt -Oz` with the feature flags the module actually uses (bulk-memory, +reference-types, nontrapping-float-to-int, sign-ext, mutable-globals, +multivalue, tail-call — deliberately *not* GC: Binaryen's bottom-type +encoding under `--enable-gc` is not parseable by V8). It then encodes the +optimized module into `wasm.ts` (generated, not hand-maintained) — the same +bundle-friendly scheme the [tty](https://github.com/bombshell-dev/tty) repo +uses: brotli at maximum quality, Z85-encoded into a self-contained +TypeScript module that embeds its own decoder and exports `compiled`, a +ready `WebAssembly.Module`. Hosts import the artifact instead of shipping +and locating a `.wasm` binary, and instantiate it synchronously (the module +is freestanding — zero imports). Verify the encoded artifact by instantiating +it and replaying the demo smoke assertions through it: + +```sh +deno run --allow-read --allow-write tasks/verify-bundle.ts +``` + +A debug build without the size pass: + +```sh +cargo build -p bombshell-css-wasm --target wasm32-unknown-unknown --release +``` + +The vendored C builds with the `cc` crate for both native and wasm32; for +wasm the build is freestanding (stub libc headers under `vendor/stubs`, a +minimal `memcpy` family in the shim, and a Rust-backed `malloc` family in +`alloc_shim.rs`). On macOS an explicit `-isysroot` is probed because +CommandLineTools/Xcode SDK resolution can break. + +## Status + +Implemented and tested: + +- **Parsing/compilation** (lexbor + Rust): full Syntax Level 3/4, nesting + (including `@nest`, nested group rules, `&` inside functional arguments), + statement at-rules (`@layer a, b;`), `@keyframes`/opaque at-rule + preservation, diagnostics with error recovery. +- **Selectors** (parcel_selectors runtime matching): tag/class/ID, + attribute operators with `i`/`s` flags, descendant/child/next-sibling/ + later-sibling combinators, `:is()`, `:where()`, `:not()` (complex args), + `:has()` (traversal), `:nth-child(An+B)`, `:nth-child(An+B of S)`, + `:first/last/only-child`, `:-of-type`, `:root`, `:empty`, `:hover`, + `:active`, `:focus`, `:focus-visible`, `:focus-within`, `:lang()`, + `:dir()`, and attribute-derivable state pseudos (`:disabled`, `:checked`, + `:required`, `:read-only/write`, `:placeholder-shown`, ...). +- **Cascade**: origin + importance ordering, `@layer` (first-appearance + order, nested paths, unlayered wins), specificity (parcel-compatible, + computed Rust-side: `:not`/`:is` take the max of their arguments, `:where` + and `:has` contribute nothing), source order. +- **Values**: canonicalized Rust-side (number minification, named colors to + shortest-hex, `light-dark()` resolved to its light branch, `font-weight` + keywords; modern color functions preserved: `oklab`/`oklch`/`lab`/`lch`/ + `color()`/`color-mix()`/relative `rgb()`), custom properties, `var()` with + fallbacks and cycle detection, CSS-wide keywords. +- **Conditions**: media (range/interval syntax, discrete features, + `not`/`and`/`or`, OR lists), container queries (named + anonymous, + `width`/`height`/`inline-size`/`block-size`/`aspect-ratio`/`orientation`, + container units `cqw`/`cqh`/... in query values), `@supports` with + compile-time leaf evaluation. + +Deliberately not implemented yet (the IR preserves the syntax; the runtime +skips it with the boundary unchanged): + +- `:nth-col()`, shadow-DOM selectors (`:host`, `::slotted`, `::part`) — + parsed but never matched. +- Pseudo-elements (`::before`, `::after`, `::selection`, ...) — parsed but + never matched; generated content is a terminal-layout question. +- Style queries (`style(color: red)`) and scroll-state queries — parsed, + preserved, not evaluated. +- Animations/transitions (`@keyframes` is preserved verbatim), `@property` + registration semantics, initial-value tables for unset properties, + `@scope` proximity, and container `em` resolution against the container's + own font-size (currently a 16px base). +- Value validation is partial: structural failures and color-property + values are checked (colors are single keywords/hashes/functions); other + property grammars are not validated (lightningcss validated everything — + a curated per-property grammar table is the eventual replacement). + +## Provenance + +- [lexbor](https://github.com/lexbor/lexbor) (Apache-2.0) is vendored under + `crates/css-compiler/vendor/lexbor` and trimmed to the syntax parser: + only `core/*` memory/string/number primitives, `css/{css,log,parser,rule}.c`, + and `css/syntax/*` are compiled (the selectors, typed at-rule/property, + and value layers are dropped — Rust owns those semantics). Local patches + to that subset: lexbor's log formatting is gutted (message text is never + read; `parser->status` is the only observable) and the token-error path + no longer serializes token text. +- `parcel_selectors` and `cssparser` are the same versions lightningcss + 1.0.0-alpha.72 depends on; selector validation and specificity come from + the same `parcel_selectors` parser the runtime matcher uses. +- Conformance cases are written against the CSSWG specifications (CSS + Nesting 1, Selectors 4, Media Queries 4, Containment 3, Cascade 5, Color + 4/5); lightningcss upstream fixtures informed several nesting and + condition shapes in the original spike. This repository's own code is + MIT. diff --git a/experiments/css/crates/css-compiler/Cargo.toml b/experiments/css/crates/css-compiler/Cargo.toml new file mode 100644 index 0000000..d2ff04d --- /dev/null +++ b/experiments/css/crates/css-compiler/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "bombshell-css-compiler" +version = "0.1.0" +description = "Lexbor-backed CSS compiler emitting the Bombshell stylesheet IR" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +build = "build.rs" + +[dependencies] +bombshell-css-core = { workspace = true, features = ["selectors"] } +cssparser = { workspace = true } + +[build-dependencies] +cc = { workspace = true } diff --git a/experiments/css/crates/css-compiler/build.rs b/experiments/css/crates/css-compiler/build.rs new file mode 100644 index 0000000..8e5032c --- /dev/null +++ b/experiments/css/crates/css-compiler/build.rs @@ -0,0 +1,122 @@ +//! Builds the vendored lexbor sources plus the front-end shim. +//! +//! For `wasm32-unknown-unknown` the build is freestanding: stub libc headers +//! under `vendor/stubs` stand in for the missing sysroot and the shim provides +//! minimal memcpy-family implementations. On native targets the system libc is +//! used; on macOS an explicit `-isysroot` works around CLT/Xcode SDK +//! resolution issues. + +use std::path::PathBuf; +use std::process::Command; + +fn main() { + let manifest = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + let manifest = PathBuf::from(manifest); + let vendor = manifest.join("vendor"); + let lexbor = vendor.join("lexbor/lexbor"); + let target = std::env::var("TARGET").unwrap(); + let freestanding = target == "wasm32-unknown-unknown"; + + let mut build = cc::Build::new(); + + build + .opt_level_str("z") + .flag("-fno-exceptions") + .include(vendor.join("lexbor")) + .file(manifest.join("c/shim.c")) + .warnings(false); + + if freestanding { + build + .flag("--target=wasm32-unknown-unknown") + .flag("-ffreestanding") + .flag("-mbulk-memory") + .define("LBX_FREESTANDING", None) + .include(vendor.join("stubs")); + } + + if target.contains("darwin") { + if let Some(sdk) = macos_sdk() { + build.flag("-isysroot").flag(&sdk); + } + } + + // Only the modules the shim actually reaches: the syntax tokenizer/parser + // and the core memory/string/number primitives they sit on. Selector + // validation and specificity are parcel_selectors' job on the Rust side + // (one parser shared with the runtime matcher), and lexbor's typed + // at-rule/property/value layers, serializers, and search structures + // (avl/bst/hash/shs) are never touched by the event-stream shim. + let keep = [ + // core primitives + "core/array.c", + "core/array_obj.c", + "core/bst.c", + "core/diyfp.c", + "core/dobject.c", + "core/mem.c", + "core/mraw.c", + "core/str.c", + "core/strtod.c", + // css front-end used by the shim + "css/css.c", + "css/log.c", + "css/parser.c", + "css/rule.c", + "css/syntax/parser.c", + "css/syntax/syntax.c", + "css/syntax/state.c", + "css/syntax/token.c", + "css/syntax/tokenizer.c", + "css/syntax/tokenizer/error.c", + "ports/posix/lexbor/core/memory.c", + ]; + + for rel in keep { + build.file(lexbor.join(rel)); + } + + build.compile("lexbor_css"); + + println!("cargo:rerun-if-changed=c/shim.c"); + println!("cargo:rerun-if-changed=vendor"); +} + +fn macos_sdk() -> Option { + if let Ok(output) = Command::new("xcrun").arg("--show-sdk-path").output() { + if output.status.success() { + let path = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if !path.is_empty() && std::path::Path::new(&path).join("usr/include").exists() { + return Some(path); + } + } + } + + // CLT/Xcode SDK resolution can break out from under us; probe known + // locations and pick the newest existing SDK. + for root in [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs", + "/Library/Developer/CommandLineTools/SDKs", + ] { + let mut candidates: Vec = std::fs::read_dir(root) + .ok()? + .filter_map(|entry| entry.ok()) + .map(|entry| entry.path()) + .filter(|path| { + path.file_name() + .and_then(|name| name.to_str()) + .map(|name| name.starts_with("MacOSX") && name.ends_with(".sdk")) + .unwrap_or(false) + }) + .map(|path| path.to_string_lossy().into_owned()) + .collect(); + candidates.sort(); + if let Some(path) = candidates.pop() { + if std::path::Path::new(&path).join("usr/include").exists() { + return Some(path); + } + } + } + + None +} diff --git a/experiments/css/crates/css-compiler/c/shim.c b/experiments/css/crates/css-compiler/c/shim.c new file mode 100644 index 0000000..c038623 --- /dev/null +++ b/experiments/css/crates/css-compiler/c/shim.c @@ -0,0 +1,799 @@ +/* + * Lexbor-backed front-end shim for the Bombshell CSS pipeline. + * + * Drives lexbor's syntax parser with custom callbacks and emits a flat event + * stream (byte spans into the original stylesheet text) for the Rust side, + * which does all semantic work: nesting expansion, condition parsing, + * canonicalization, and IR emission. + * + * Event kinds: + * LBX_STYLE_BEGIN a = prelude span flags bit1 = failed + * LBX_STYLE_END (no data) + * LBX_AT_BEGIN a = at-rule name span + * LBX_AT_PRELUDE a = prelude span flags bit1 = failed + * LBX_AT_END a = full rule span flags bit0 = has_block + * LBX_DECL a = name span, b = value span + * flags bit0 = important, bit1 = failed + */ +#include +#include + +#ifdef LBX_FREESTANDING +/* Minimal libc for wasm32-unknown-unknown; word-wise for speed. */ +void * +memcpy(void *d, const void *s, unsigned long n) +{ + unsigned char *dd = d; + const unsigned char *ss = s; + + for (; n >= 8; n -= 8, dd += 8, ss += 8) { + __builtin_memcpy(dd, ss, 8); + } + + while (n--) { + *dd++ = *ss++; + } + + return d; +} + +void * +memmove(void *d, const void *s, unsigned long n) +{ + unsigned char *dd = d; + const unsigned char *ss = s; + + if ((uintptr_t) dd < (uintptr_t) ss) { + return memcpy(d, s, n); + } + + dd += n; + ss += n; + + for (; n >= 8; n -= 8, dd -= 8, ss -= 8) { + __builtin_memcpy(dd - 8, ss - 8, 8); + } + + while (n--) { + *--dd = *--ss; + } + + return d; +} + +void * +memset(void *d, int c, unsigned long n) +{ + unsigned char *dd = d; + unsigned char v = (unsigned char) c; + + for (; n >= 8; n -= 8, dd += 8) { + __builtin_memset(dd, v, 8); + } + + while (n--) { + *dd++ = v; + } + + return d; +} + +int +memcmp(const void *a, const void *b, unsigned long n) +{ + const unsigned char *x = a; + const unsigned char *y = b; + + while (n--) { + if (*x != *y) { + return *x - *y; + } + + x++; + y++; + } + + return 0; +} + +unsigned long +strlen(const char *s) +{ + const char *p = s; + + while (*p) { + p++; + } + + return (unsigned long) (p - s); +} +#endif /* LBX_FREESTANDING */ + +#define LBX_STYLE_BEGIN 0 +#define LBX_STYLE_END 1 +#define LBX_AT_BEGIN 2 +#define LBX_AT_PRELUDE 3 +#define LBX_AT_END 4 +#define LBX_DECL 5 + +#define LBX_FLAG_HAS_BLOCK 0x01 +#define LBX_FLAG_IMPORTANT 0x01 +#define LBX_FLAG_FAILED 0x02 + +typedef struct { + uint8_t kind; + uint8_t flags; + uint32_t a0; + uint32_t a1; + uint32_t b0; + uint32_t b1; +} +lbx_event_t; + +typedef struct { + const unsigned char *css; + size_t css_len; + + lbx_event_t *events; + size_t events_len; + size_t events_cap; + + uintptr_t prelude_begin; + uintptr_t prelude_end; + bool prelude_seen; + + uintptr_t decl_name_begin; + uintptr_t decl_name_end; + + uintptr_t q_begin; + bool q_open; + + struct { + uintptr_t begin; + bool has_block; + } + at_stack[64]; + size_t at_depth; + + bool oom; +} +lbx_ctx_t; + +static void lbx_emit(lbx_ctx_t *ctx, uint8_t kind, uint8_t flags, + uintptr_t a0, uintptr_t a1, uintptr_t b0, uintptr_t b1); + +static bool lbx_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +static bool lbx_next_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t lbx_noop_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static bool lbx_at_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +static lxb_status_t lbx_at_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static lxb_status_t lbx_at_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static const lxb_css_syntax_cb_at_rule_t *lbx_begin_at_rule( + lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static bool lbx_qualified_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t lbx_qualified_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static lxb_status_t lbx_qualified_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static const lxb_css_syntax_cb_qualified_rule_t *lbx_begin_qualified( + lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static const lxb_css_syntax_cb_declarations_t *lbx_begin_declarations( + lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static const lxb_css_syntax_cb_block_t *lbx_begin_block( + lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static lxb_css_parser_state_f lbx_decl_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +static bool lbx_decl_value_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t lbx_decl_end(lxb_css_parser_t *parser, void *declaration, + void *context, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed); + +static lbx_ctx_t lbx_ctx; + +static const lxb_css_syntax_cb_list_rules_t lbx_list_rules_cb; +static const lxb_css_syntax_cb_at_rule_t lbx_at_rule_cb; +static const lxb_css_syntax_cb_qualified_rule_t lbx_qualified_rule_cb; +static const lxb_css_syntax_cb_block_t lbx_block_cb; +static const lxb_css_syntax_cb_declarations_t lbx_declarations_cb; + +static uintptr_t +lbx_clamp(lbx_ctx_t *ctx, uintptr_t off) +{ + uintptr_t base = (uintptr_t) ctx->css; + + if (off < base || off > base + ctx->css_len) { + return base + ctx->css_len; + } + + return off; +} + +static void +lbx_emit(lbx_ctx_t *ctx, uint8_t kind, uint8_t flags, + uintptr_t a0, uintptr_t a1, uintptr_t b0, uintptr_t b1) +{ + if (ctx->oom) { + return; + } + + /* Synthetic tokens (e.g. _END) carry pointers outside the input; clamp + * spans to the stylesheet bounds. */ + a0 = lbx_clamp(ctx, a0); + a1 = lbx_clamp(ctx, a1); + b0 = lbx_clamp(ctx, b0); + b1 = lbx_clamp(ctx, b1); + + if (ctx->events_len == ctx->events_cap) { + size_t cap = ctx->events_cap == 0 ? 256 : ctx->events_cap * 2; + lbx_event_t *events = lexbor_realloc(ctx->events, cap * sizeof(lbx_event_t)); + + if (events == NULL) { + ctx->oom = true; + return; + } + + ctx->events = events; + ctx->events_cap = cap; + } + + lbx_event_t *event = &ctx->events[ctx->events_len++]; + + event->kind = kind; + event->flags = flags; + event->a0 = (uint32_t) (a0 - (uintptr_t) ctx->css); + event->a1 = (uint32_t) (a1 - (uintptr_t) ctx->css); + event->b0 = (uint32_t) (b0 - (uintptr_t) ctx->css); + event->b1 = (uint32_t) (b1 - (uintptr_t) ctx->css); +} + +static void +lbx_prelude_reset(lbx_ctx_t *ctx) +{ + ctx->prelude_begin = 0; + ctx->prelude_end = 0; + ctx->prelude_seen = false; +} + +/* Consume every remaining token of the current phase. */ +static bool +lbx_consume_all(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +/* State callbacks ---------------------------------------------------------- */ + +static bool +lbx_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lbx_consume_all(parser, token); +} + +/* `next` states run after a child rule completes; the parser owns the + * current token, so they must not consume. */ +static bool +lbx_next_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + (void) token; + (void) ctx; + + return lxb_css_parser_success(parser); +} + +static bool +lbx_decl_value_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lbx_consume_all(parser, token); +} + +/* Prelude readers: accumulate the span, then consume. */ + +static bool +lbx_read_prelude(lbx_ctx_t *ctx, lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + uintptr_t begin = (uintptr_t) lxb_css_syntax_token_base(token)->begin; + uintptr_t end = begin + lxb_css_syntax_token_base(token)->length; + + if (!ctx->prelude_seen) { + ctx->prelude_seen = true; + ctx->prelude_begin = begin; + ctx->prelude_end = end; + } + else { + if (begin < ctx->prelude_begin) { + ctx->prelude_begin = begin; + } + if (end > ctx->prelude_end) { + ctx->prelude_end = end; + } + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static bool +lbx_at_prelude(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lbx_read_prelude((lbx_ctx_t *) ctx, parser, token); +} + +static bool +lbx_qualified_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lbx_read_prelude((lbx_ctx_t *) ctx, parser, token); +} + +/* Done callbacks ----------------------------------------------------------- */ + +static lxb_status_t +lbx_noop_end(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static lxb_status_t +lbx_at_prelude_end(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *context, bool failed) +{ + lbx_ctx_t *ctx = context; + uint8_t flags = failed ? LBX_FLAG_FAILED : 0; + + if (ctx->prelude_seen) { + lbx_emit(ctx, LBX_AT_PRELUDE, flags, + ctx->prelude_begin, ctx->prelude_end, 0, 0); + } + else if (ctx->at_depth > 0) { + /* The prelude state never ran; reconstruct the span from the + * at-keyword start to the terminator token (`;`, `{`, or EOF). */ + uintptr_t begin = ctx->at_stack[ctx->at_depth - 1].begin; + uintptr_t end = (uintptr_t) lxb_css_syntax_token_base(token)->begin; + + if (end > begin) { + lbx_emit(ctx, LBX_AT_PRELUDE, flags, begin, end, 0, 0); + } + } + + lbx_prelude_reset(ctx); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lbx_at_end(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *context, bool failed) +{ + lbx_ctx_t *ctx = context; + uint8_t flags = failed ? LBX_FLAG_FAILED : 0; + + if (ctx->at_depth == 0) { + return LXB_STATUS_OK; + } + + ctx->at_depth--; + + uintptr_t begin = ctx->at_stack[ctx->at_depth].begin; + uintptr_t end = (uintptr_t) lxb_css_syntax_token_base(token)->begin + + lxb_css_syntax_token_base(token)->length; + + if (end > ctx->css_len + (uintptr_t) ctx->css) { + end = ctx->css_len + (uintptr_t) ctx->css; + } + + if (ctx->at_stack[ctx->at_depth].has_block) { + flags |= LBX_FLAG_HAS_BLOCK; + } + + lbx_emit(ctx, LBX_AT_END, flags, begin, end, 0, 0); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lbx_qualified_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *context, + bool failed) +{ + lbx_ctx_t *ctx = context; + + uint8_t flags = failed ? LBX_FLAG_FAILED : 0; + + if (ctx->prelude_seen) { + lbx_emit(ctx, LBX_STYLE_BEGIN, flags, + ctx->prelude_begin, ctx->prelude_end, 0, 0); + } + + lbx_prelude_reset(ctx); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lbx_qualified_end(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *context, bool failed) +{ + lbx_emit((lbx_ctx_t *) context, LBX_STYLE_END, 0, 0, 0, 0, 0); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lbx_decl_end(lxb_css_parser_t *parser, void *declaration, void *context, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed) +{ + lbx_ctx_t *ctx = context; + uint8_t flags = 0; + + if (important) { + flags |= LBX_FLAG_IMPORTANT; + } + + if (failed) { + flags |= LBX_FLAG_FAILED; + } + + uintptr_t value_begin = (uintptr_t) ctx->css + offset->value_begin; + uintptr_t value_end = (uintptr_t) ctx->css + offset->value_end; + + lbx_emit(ctx, LBX_DECL, flags, ctx->decl_name_begin, ctx->decl_name_end, + value_begin, value_end); + + return LXB_STATUS_OK; +} + +/* Begin callbacks ---------------------------------------------------------- */ + +static const lxb_css_syntax_cb_at_rule_t * +lbx_begin_at_rule(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lbx_ctx_t *c = &lbx_ctx; + + (void) ctx; + + if (c->at_depth == sizeof(c->at_stack) / sizeof(c->at_stack[0])) { + c->oom = true; + return NULL; + } + + uintptr_t begin = (uintptr_t) lxb_css_syntax_token_base(token)->begin; + + c->at_stack[c->at_depth].begin = begin; + c->at_stack[c->at_depth].has_block = false; + c->at_depth++; + + lbx_emit(c, LBX_AT_BEGIN, 0, begin, + begin + lxb_css_syntax_token_base(token)->length, 0, 0); + + *out_rule = c; + + return &lbx_at_rule_cb; +} + +static const lxb_css_syntax_cb_qualified_rule_t * +lbx_begin_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule) +{ + (void) ctx; + *out_rule = &lbx_ctx; + + return &lbx_qualified_rule_cb; +} + +static const lxb_css_syntax_cb_declarations_t * +lbx_begin_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule) +{ + (void) parser; + (void) token; + (void) ctx; + *out_rule = &lbx_ctx; + + return &lbx_declarations_cb; +} + +static const lxb_css_syntax_cb_block_t * +lbx_begin_block(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lbx_ctx_t *c = &lbx_ctx; + + (void) parser; + (void) token; + + + if (c->at_depth > 0) { + c->at_stack[c->at_depth - 1].has_block = true; + } + + (void) parser; + (void) token; + *out_rule = &lbx_ctx; + + return &lbx_block_cb; +} + +/* Declaration name: record the span, return the value state. */ + +static lxb_css_parser_state_f +lbx_decl_name(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lbx_ctx_t *c = ctx; + + c->decl_name_begin = (uintptr_t) lxb_css_syntax_token_base(token)->begin; + c->decl_name_end = c->decl_name_begin + + lxb_css_syntax_token_base(token)->length; + + *out_rule = &lbx_ctx; + + return lbx_decl_value_state; +} + +/* Callback structs ---------------------------------------------------------- */ + +static const lxb_css_syntax_cb_list_rules_t lbx_list_rules_cb = { + .cb = { + .failed = lbx_state_success, + .end = lbx_noop_end, + }, + .next = lbx_next_success, + .at_rule = lbx_begin_at_rule, + .qualified_rule = lbx_begin_qualified, +}; + +static const lxb_css_syntax_cb_at_rule_t lbx_at_rule_cb = { + .cb = { + .failed = lbx_state_success, + .end = lbx_at_end, + }, + .prelude = lbx_at_prelude, + .prelude_end = lbx_at_prelude_end, + .block = lbx_begin_block, +}; + +static const lxb_css_syntax_cb_qualified_rule_t lbx_qualified_rule_cb = { + .cb = { + .failed = lbx_state_success, + .end = lbx_qualified_end, + }, + .prelude = lbx_qualified_prelude, + .prelude_end = lbx_qualified_prelude_end, + .block = lbx_begin_block, +}; + +static const lxb_css_syntax_cb_block_t lbx_block_cb = { + .cb = { + .failed = lbx_state_success, + .end = lbx_noop_end, + }, + .next = lbx_next_success, + .at_rule = lbx_begin_at_rule, + .declarations = lbx_begin_declarations, + .qualified_rule = lbx_begin_qualified, +}; + +static const lxb_css_syntax_cb_declarations_t lbx_declarations_cb = { + .cb = { + .failed = lbx_state_success, + .end = lbx_noop_end, + }, + .name = lbx_decl_name, + .end = lbx_decl_end, +}; + +/* Entry points --------------------------------------------------------------- */ + +static lbx_ctx_t lbx_ctx; + +int +lbx_parse(const unsigned char *css, size_t css_len, + lbx_event_t **out_events, size_t *out_len) +{ + lxb_css_parser_t *parser; + lxb_css_memory_t *memory; + lxb_status_t status; + + *out_events = NULL; + *out_len = 0; + + memset(&lbx_ctx, 0, sizeof(lbx_ctx_t)); + + lbx_ctx.css = css; + lbx_ctx.css_len = css_len; + + parser = lxb_css_parser_create(); + if (parser == NULL) { + return 1; + } + + status = lxb_css_parser_init(parser, NULL); + if (status != LXB_STATUS_OK) { + lxb_css_parser_destroy(parser, true); + return 1; + } + + memory = lxb_css_memory_create(); + if (memory == NULL) { + lxb_css_parser_destroy(parser, true); + return 1; + } + + status = lxb_css_memory_init(memory, 1024); + if (status != LXB_STATUS_OK) { + lxb_css_memory_destroy(memory, true); + lxb_css_parser_destroy(parser, true); + return 1; + } + + lxb_css_parser_memory_set(parser, memory); + + if (lxb_css_syntax_parse_list_rules(parser, &lbx_list_rules_cb, + (const lxb_char_t *) css, css_len) == NULL + && parser->status != LXB_STATUS_OK && !lbx_ctx.oom) + { + lexbor_free(lbx_ctx.events); + lxb_css_memory_destroy(memory, true); + lxb_css_parser_destroy(parser, true); + return 1; + } + + lxb_css_memory_destroy(memory, true); + lxb_css_parser_destroy(parser, true); + + if (lbx_ctx.oom) { + lexbor_free(lbx_ctx.events); + return 1; + } + + *out_events = lbx_ctx.events; + *out_len = lbx_ctx.events_len; + + + return 0; +} + +void +lbx_free_events(lbx_event_t *events, size_t len) +{ + lexbor_free(events); +} + +/* + * Compat shims for the trimmed vendor build. The legacy css/state.c and the + * typed at-rule/property layers are not compiled; only these handful of + * symbols remain referenced by the syntax parser: + * + * - lxb_css_state_success is the parser's default running state. + * - The typed destroy hooks are only reached through lxb_css_rule_destroy's + * switch arms for payloads this shim never builds (rule contexts are the + * parse context, so `u.*` members are always NULL); real lexbor frees + * nothing for a NULL payload either. + */ +bool +lxb_css_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + return true; + case LXB_CSS_SYNTAX_TOKEN__END: + return true; + default: + return lxb_css_parser_failed(parser); + } +} + +bool +lxb_css_state_blank(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_parser_success(parser); +} + +bool +lxb_css_state_stop(lxb_css_parser_t *parser, const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_parser_stop(parser); +} + +void * +lxb_css_at_rule__undef_destroy(lxb_css_memory_t *memory, void *style, + bool self_destroy) +{ + (void) memory; + (void) style; + (void) self_destroy; + + return NULL; +} + +void * +lxb_css_at_rule_media_destroy(lxb_css_memory_t *memory, void *style, + bool self_destroy) +{ + (void) memory; + (void) style; + (void) self_destroy; + + return NULL; +} + +void * +lxb_css_at_rule_namespace_destroy(lxb_css_memory_t *memory, void *style, + bool self_destroy) +{ + (void) memory; + (void) style; + (void) self_destroy; + + return NULL; +} + +void * +lxb_css_property_destroy(lxb_css_memory_t *memory, void *style, + int type, bool self_destroy) +{ + (void) memory; + (void) style; + (void) type; + (void) self_destroy; + + return NULL; +} + +void +lxb_css_selector_list_destroy(lxb_css_selector_list_t *list) +{ + (void) list; +} diff --git a/experiments/css/crates/css-compiler/examples/compile_time.rs b/experiments/css/crates/css-compiler/examples/compile_time.rs new file mode 100644 index 0000000..bae63ea --- /dev/null +++ b/experiments/css/crates/css-compiler/examples/compile_time.rs @@ -0,0 +1,31 @@ +//! Compile-throughput probe for the bench corpus shape. + +use std::time::Instant; + +use bombshell_css_compiler::{CompileOptions, compile}; +use bombshell_css_core::Origin; + +fn main() { + let css = std::fs::read_to_string( + std::env::args().nth(1).expect("corpus path argument"), + ) + .expect("corpus file"); + + for _ in 0..5 { + let _ = compile(&css, Origin::Author, CompileOptions::default()); + } + let mut times = Vec::new(); + for _ in 0..30 { + let start = Instant::now(); + let out = compile(&css, Origin::Author, CompileOptions::default()); + times.push(start.elapsed()); + let _ = out; + } + times.sort(); + let median = times[times.len() / 2]; + println!( + "native compile: median {:.2?} → {:.1} MB/s", + median, + css.len() as f64 / median.as_secs_f64() / 1_048_576.0 + ); +} diff --git a/experiments/css/crates/css-compiler/examples/events.rs b/experiments/css/crates/css-compiler/examples/events.rs new file mode 100644 index 0000000..9728dc6 --- /dev/null +++ b/experiments/css/crates/css-compiler/examples/events.rs @@ -0,0 +1,60 @@ +use bombshell_css_compiler::{CompileOptions, compile}; +use bombshell_css_core::Origin; + +fn main() { + if let Some(path) = std::env::args().nth(2) { + let css = std::fs::read_to_string(&path).unwrap(); + let out = compile(&css, Origin::Author, Default::default()); + let sheet = out.sheet.unwrap(); + for rule in &sheet.rules { + println!("{} media={:#?}", rule.selector, rule.media); + } + if !sheet.warnings.is_empty() { + println!("warnings: {:#?}", sheet.warnings); + } + return; + } + let skip: usize = std::env::args() + .nth(1) + .and_then(|a| a.parse().ok()) + .unwrap_or(0); + for css in [ + ".a { color: red }", + "@layer base;", + "@layer base.type { .a { color: red } }", + "@media (min-width: 24em) { .a { color: red } }", + "@media (min-width: 24em) { @container panel (inline-size > 45em) { @supports (color: oklch(70% 0.2 40)) { .b { color: blue } } } }", + ".card { color: red; & > .title { font-weight: bold } .body { color: blue } }", + "@layer base;\n@layer base.type { .a { color: red } }", + "@keyframes pulse { from { opacity: 1 } to { opacity: 0 } }\n@font-face { font-family: X; src: url(x.woff2) }", + ".a { color: red } .a:hover { color: blue }", + ".card { color: oklch(70% 0.2 40); & > .title { font-weight: bold; &:hover { text-decoration: underline; } } }", + "@media (prefers-color-scheme: dark) { .a { color: green } }", + "@media (prefers-reduced-motion: reduce) { .a { color: blue } }", + "@media screen and (not (hover)) { .a { color: orange } }", + ".btn { color: gray }\n.btn:hover { color: white }\n.btn:active { color: black }\n.btn:focus { outline: 1px }\n.btn:focus-within { border-color: cyan }", + ".item:first-child { color: blue }\n.root .item { color: green }", + ".item:nth-child(2n + 1 of .tagged) { color: yellow }\n.item:not(.skip):not(.list > *) { color: blue }", + ":root { --brand: oklch(70% .2 40); --gap: 8px; color: var(--brand) }\n.box { border-color: var(--brand); margin: var(--gap) }\n.box { --gap: 4px }", + ] + .into_iter() + .enumerate() + .skip(skip) + { + let (i, css) = css; + println!("[case {i}]"); + let out = compile(css, Origin::Author, CompileOptions::default()); + match &out.sheet { + Some(sheet) => { + println!("--- {:?}\n rules={} layers={:?} keyframes={} opaque={}", css, sheet.rules.len(), sheet.layer_order, sheet.keyframes.len(), sheet.opaque.len()); + for rule in &sheet.rules { + println!(" {} sp={:#x} decls={:?}", rule.selector, rule.specificity, rule.declarations); + } + } + None => println!("--- {:?}\n FAILED {:?}", css, out.diagnostics), + } + if !out.diagnostics.is_empty() { + println!(" diags: {:?}", out.diagnostics); + } + } +} diff --git a/experiments/css/crates/css-compiler/src/alloc_shim.rs b/experiments/css/crates/css-compiler/src/alloc_shim.rs new file mode 100644 index 0000000..ae98167 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/alloc_shim.rs @@ -0,0 +1,100 @@ +//! C allocator for freestanding wasm32. +//! +//! lexbor's allocator calls `malloc`/`free`/`calloc`/`realloc`; on +//! `wasm32-unknown-unknown` there is no libc, so these are backed by the +//! Rust global allocator. Every allocation carries a size header so `free` +//! can reconstruct the layout (alignment is fixed at the header's). + +#![cfg(target_arch = "wasm32")] + +use std::alloc::{Layout, alloc, alloc_zeroed, dealloc}; + +#[repr(C)] +struct Header { + size: usize, +} + +const HEADER_ALIGN: usize = core::mem::align_of::
(); + +fn padded(size: usize) -> usize { + (size + HEADER_ALIGN - 1) & !(HEADER_ALIGN - 1) +} + +#[unsafe(no_mangle)] +pub extern "C" fn malloc(size: usize) -> *mut u8 { + let size = size.max(1); + let Ok(layout) = Layout::from_size_align(size_of::
() + padded(size), HEADER_ALIGN) + else { + return std::ptr::null_mut(); + }; + + unsafe { + let base = alloc(layout); + if base.is_null() { + return std::ptr::null_mut(); + } + *(base as *mut Header) = Header { size }; + base.add(size_of::
()) + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn free(pointer: *mut u8) { + if pointer.is_null() { + return; + } + + unsafe { + let header = pointer.sub(size_of::
()) as *mut Header; + let size = (*header).size; + if let Ok(layout) = Layout::from_size_align( + size_of::
() + padded(size), + HEADER_ALIGN, + ) { + dealloc(pointer.sub(size_of::
()), layout); + } + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn calloc(count: usize, size: usize) -> *mut u8 { + let Some(total) = count.checked_mul(size) else { + return std::ptr::null_mut(); + }; + let total = total.max(1); + let Ok(layout) = Layout::from_size_align(size_of::
() + padded(total), HEADER_ALIGN) + else { + return std::ptr::null_mut(); + }; + + unsafe { + let base = alloc_zeroed(layout); + if base.is_null() { + return std::ptr::null_mut(); + } + *(base as *mut Header) = Header { size: total }; + base.add(size_of::
()) + } +} + +#[unsafe(no_mangle)] +pub extern "C" fn realloc(pointer: *mut u8, size: usize) -> *mut u8 { + if pointer.is_null() { + return malloc(size); + } + + let old_size = unsafe { (*(pointer.sub(size_of::
()) as *const Header)).size }; + + let new = malloc(size); + if new.is_null() { + return std::ptr::null_mut(); + } + + unsafe { + std::ptr::copy_nonoverlapping(pointer, new, old_size.min(size)); + } + + free(pointer); + + new +} \ No newline at end of file diff --git a/experiments/css/crates/css-compiler/src/canonical.rs b/experiments/css/crates/css-compiler/src/canonical.rs new file mode 100644 index 0000000..12edfa9 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/canonical.rs @@ -0,0 +1,236 @@ +//! Canonical declaration values. +//! +//! The previous lightningcss-backed front-end serialized every value through +//! its printer with `minify: true`. This module reproduces the observable +//! contract with token-level re-serialization: numbers lose redundant +//! leading/trailing zeros (`0.2` → `.2`), and color-property values are +//! validated (a color is a single component: keyword, hash, or function) +//! so invalid declarations report through the recovery paths like they used +//! to. + +use bombshell_css_core::Diagnostic; +use bombshell_css_core::tokens::write_token; +use cssparser::{Parser as CssParser, ParserInput, Token}; + +use crate::colors::named_color; + +/// Canonicalize a declaration value; `Err` marks the declaration invalid. +pub(crate) fn canonicalize(name: &str, value: &str) -> Result { + // Custom properties keep their value verbatim (substituted text is + // resolved at computed-value time). + if name.starts_with("--") { + return Ok(value.to_string()); + } + + // CSS-wide keywords are valid for every property. + match value.trim() { + "inherit" | "initial" | "unset" | "revert" | "revert-layer" => { + return Ok(value.trim().to_string()); + } + _ => {} + } + + if name == "color" || name.ends_with("-color") { + // `light-dark(a, b)` resolves to its light branch at compile time + // (the previous front-end's printer behavior). + let value = match resolve_light_dark(value) { + Some(light) => light, + None => value.to_string(), + }; + + validate_color(&value)?; + + // The shortest representation wins; ties go to hex (the previous + // front-end's printer behavior: `red` stays, `yellow` → `#ff0`). + let trimmed = value.trim(); + if let Some(hex) = named_color(trimmed.to_ascii_lowercase().as_str()) { + return Ok(if trimmed.len() < hex.len() { + trimmed.to_ascii_lowercase() + } else { + hex.to_string() + }); + } + + return Ok(minify_value(&value)); + } + + if name == "font-weight" { + return Ok(match value.trim() { + "bold" => "700".to_string(), + "normal" => "400".to_string(), + other => other.to_string(), + }); + } + + Ok(minify_value(value)) +} + +/// `light-dark(a, b)` → its first argument. +fn resolve_light_dark(value: &str) -> Option { + let trimmed = value.trim(); + let rest = trimmed.strip_prefix("light-dark(")?; + let rest = rest.strip_suffix(')')?; + + let mut depth = 0usize; + let mut quote: Option = None; + for (i, &b) in rest.as_bytes().iter().enumerate() { + match quote { + Some(q) if b == q => quote = None, + Some(_) => {} + None => match b { + b'"' | b'\'' => quote = Some(b), + b'(' => depth += 1, + b')' => depth = depth.saturating_sub(1), + b',' if depth == 0 => return Some(rest[..i].trim().to_string()), + _ => {} + }, + } + } + + None +} + +/// A color value is one component: a keyword, a hash, or a balanced +/// function call. +/// +/// Hex payloads may be 3, 4, 6, or 8 hex digits (`#abc`, `#abcd`, +/// `#aabbcc`, `#aabbccdd`). +fn is_valid_hex(hash: &str) -> bool { + let bytes = hash.as_bytes(); + matches!(bytes.len(), 3 | 4 | 6 | 8) + && bytes.iter().all(|b| b.is_ascii_hexdigit()) +} + +fn validate_color(value: &str) -> Result<(), Diagnostic> { + let mut input = ParserInput::new(value); + let mut parser = CssParser::new(&mut input); + + let valid = parser + .try_parse(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + match parser.next_including_whitespace() { + // Hex colors tokenize as `Hash` when the payload starts with a + // digit (`#333`) and as `IDHash` when it reads as an + // identifier (`#fff`); both are the same value grammar, + // and both still require hex-digit syntax. + Ok(Token::Hash(hash) | Token::IDHash(hash)) => { + if !is_valid_hex(hash) { + return Err(cssparser::ParseError { + kind: cssparser::ParseErrorKind::Custom(()), + location: parser.current_source_location(), + }); + } + } + Ok(Token::Ident(ident)) => { + let name = ident.to_ascii_lowercase(); + if named_color(&name).is_none() + && name != "currentcolor" + && name != "transparent" + { + return Err(cssparser::ParseError { + kind: cssparser::ParseErrorKind::Custom(()), + location: parser.current_source_location(), + }); + } + } + Ok(Token::Function(_)) => { + // Modern spaces accept any balanced function here; the + // runtime evaluates the text. + parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + loop { + match parser.next_including_whitespace() { + Ok(_) => {} + Err(_) => break, + } + } + Ok(()) + }, + )?; + } + _ => { + return Err(cssparser::ParseError { + kind: cssparser::ParseErrorKind::Custom(()), + location: parser.current_source_location(), + }); + } + } + + // Nothing else may follow. + match parser.next_including_whitespace() { + Err(_) => Ok(()), + Ok(_) => Err(cssparser::ParseError { + kind: cssparser::ParseErrorKind::Custom(()), + location: parser.current_source_location(), + }), + } + }) + .is_ok(); + + if valid { + Ok(()) + } else { + Err(Diagnostic::warning( + "declaration", + { + let mut message = String::from("invalid color value `"); + message.push_str(value); + message.push('`'); + message + }, + )) + } +} + +/// Re-serialize a value: whitespace collapses to single spaces and numeric +/// tokens drop redundant zeros. +pub(crate) fn minify_value(value: &str) -> String { + let mut input = ParserInput::new(value); + let mut parser = CssParser::new(&mut input); + let mut out = String::with_capacity(value.len()); + write_value_tokens(&mut parser, &mut out); + out +} + +fn write_value_tokens(parser: &mut CssParser<'_, '_>, out: &mut String) { + let mut pending_space = false; + + loop { + match parser.next_including_whitespace_and_comments() { + Ok(token) => match token { + Token::Comment(_) => {} + Token::WhiteSpace(_) => pending_space = true, + Token::Function(_) + | Token::ParenthesisBlock + | Token::SquareBracketBlock + | Token::CurlyBracketBlock => { + if pending_space && !out.is_empty() { + out.push(' '); + } + pending_space = false; + write_token(out, &token); + let closer = match token { + Token::SquareBracketBlock => ']', + Token::CurlyBracketBlock => '}', + _ => ')', + }; + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_value_tokens(parser, out); + Ok(()) + }, + ); + out.push(closer); + } + other => { + if pending_space && !out.is_empty() { + out.push(' '); + } + pending_space = false; + write_token(out, &other); + } + }, + Err(_) => break, + } + } +} + diff --git a/experiments/css/crates/css-compiler/src/colors.rs b/experiments/css/crates/css-compiler/src/colors.rs new file mode 100644 index 0000000..c7e3203 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/colors.rs @@ -0,0 +1,54 @@ +//! Canonical color keyword table (CSS Color 3 named colors). +//! +//! lightningcss canonicalized named colors to hex at compile time; the +//! runtime consumes hex/functional colors directly, so this table keeps +//! the IR byte-for-byte compatible with the previous front-end. + +/// Shortest-hex form for every CSS named color (lowercase). +pub(crate) fn named_color(name: &str) -> Option<&'static str> { + // Packed storage: one blob plus u16 (name, hex) offsets. The binary + // search compares the same slices the old pointer-array table did; + // packing only changes how the strings live in linear memory. + OFFSETS + .binary_search_by(|&(name_off, hex_off)| { + let end = hex_off - 1; + BLOB[name_off as usize..end as usize].cmp(name) + }) + .ok() + .map(|index| { + let (_, hex_off) = OFFSETS[index]; + let end = OFFSETS + .get(index + 1) + .map(|&(next_name, _)| next_name - 1) + .unwrap_or(BLOB.len() as u16); + &BLOB[hex_off as usize..end as usize] + }) +} + +const BLOB: &str = "aliceblue\0#f0f8ff\0antiquewhite\0#faebd7\0aqua\0#0ff\0aquamarine\0#7fffd4\0azure\0#f0ffff\0beige\0#f5f5dc\0bisque\0#ffe4c4\0black\0#000\0blanchedalmond\0#ffebcd\0blue\0#00f\0blueviolet\0#8a2be2\0brown\0#a52a2a\0burlywood\0#deb887\0cadetblue\0#5f9ea0\0chartreuse\0#7fff00\0chocolate\0#d2691e\0coral\0#ff7f50\0cornflowerblue\0#6495ed\0cornsilk\0#fff8dc\0crimson\0#dc143c\0cyan\0#0ff\0darkblue\0#00008b\0darkcyan\0#008b8b\0darkgoldenrod\0#b8860b\0darkgray\0#a9a9a9\0darkgreen\0#006400\0darkgrey\0#a9a9a9\0darkkhaki\0#bdb76b\0darkmagenta\0#8b008b\0darkolivegreen\0#556b2f\0darkorange\0#ff8c00\0darkorchid\0#9932cc\0darkred\0#8b0000\0darksalmon\0#e9967a\0darkseagreen\0#8fbc8f\0darkslateblue\0#483d8b\0darkslategray\0#2f4f4f\0darkslategrey\0#2f4f4f\0darkturquoise\0#00ced1\0darkviolet\0#9400d3\0deeppink\0#ff1493\0deepskyblue\0#00bfff\0dimgray\0#696969\0dimgrey\0#696969\0dodgerblue\0#1e90ff\0firebrick\0#b22222\0floralwhite\0#fffaf0\0forestgreen\0#228b22\0fuchsia\0#f0f\0gainsboro\0#dcdcdc\0ghostwhite\0#f8f8ff\0gold\0#ffd700\0goldenrod\0#daa520\0gray\0#808080\0green\0#008000\0greenyellow\0#adff2f\0grey\0#808080\0honeydew\0#f0fff0\0hotpink\0#ff69b4\0indianred\0#cd5c5c\0indigo\0#4b0082\0ivory\0#fffff0\0khaki\0#f0e68c\0lavender\0#e6e6fa\0lavenderblush\0#fff0f5\0lawngreen\0#7cfc00\0lemonchiffon\0#fffacd\0lightblue\0#add8e6\0lightcoral\0#f08080\0lightcyan\0#e0ffff\0lightgoldenrodyellow\0#fafad2\0lightgray\0#d3d3d3\0lightgreen\0#90ee90\0lightgrey\0#d3d3d3\0lightpink\0#ffb6c1\0lightsalmon\0#ffa07a\0lightseagreen\0#20b2aa\0lightskyblue\0#87cefa\0lightslategray\0#778899\0lightslategrey\0#778899\0lightsteelblue\0#b0c4de\0lightyellow\0#ffffe0\0lime\0#0f0\0limegreen\0#32cd32\0linen\0#faf0e6\0magenta\0#f0f\0maroon\0#800000\0mediumaquamarine\0#66cdaa\0mediumblue\0#0000cd\0mediumorchid\0#ba55d3\0mediumpurple\0#9370db\0mediumseagreen\0#3cb371\0mediumslateblue\0#7b68ee\0mediumspringgreen\0#00fa9a\0mediumturquoise\0#48d1cc\0mediumvioletred\0#c71585\0midnightblue\0#191970\0mintcream\0#f5fffa\0mistyrose\0#ffe4e1\0moccasin\0#ffe4b5\0navajowhite\0#ffdead\0navy\0#000080\0oldlace\0#fdf5e6\0olive\0#808000\0olivedrab\0#6b8e23\0orange\0#ffa500\0orangered\0#ff4500\0orchid\0#da70d6\0palegoldenrod\0#eee8aa\0palegreen\0#98fb98\0paleturquoise\0#afeeee\0palevioletred\0#db7093\0papayawhip\0#ffefd5\0peachpuff\0#ffdab9\0peru\0#cd853f\0pink\0#ffc0cb\0plum\0#dda0dd\0powderblue\0#b0e0e6\0purple\0#800080\0rebeccapurple\0#663399\0red\0#f00\0rosybrown\0#bc8f8f\0royalblue\0#4169e1\0saddlebrown\0#8b4513\0salmon\0#fa8072\0sandybrown\0#f4a460\0seagreen\0#2e8b57\0seashell\0#fff5ee\0sienna\0#a0522d\0silver\0#c0c0c0\0skyblue\0#87ceeb\0slateblue\0#6a5acd\0slategray\0#708090\0slategrey\0#708090\0snow\0#fffafa\0springgreen\0#00ff7f\0steelblue\0#4682b4\0tan\0#d2b48c\0teal\0#008080\0thistle\0#d8bfd8\0tomato\0#ff6347\0turquoise\0#40e0d0\0violet\0#ee82ee\0wheat\0#f5deb3\0white\0#fff\0whitesmoke\0#f5f5f5\0yellow\0#ff0\0yellowgreen\0#9acd32\0"; + +/// (name offset, hex offset) into [`BLOB`], sorted by name; the hex runs +/// to the next entry's name (or the end). +const OFFSETS: &[(u16, u16)] = &[(0, 10), (18, 31), (39, 44), (49, 60), (68, 74), (82, 88), (96, 103), (111, 117), (122, 137), (145, 150), (155, 166), (174, 180), (188, 198), (206, 216), (224, 235), (243, 253), (261, 267), (275, 290), (298, 307), (315, 323), (331, 336), (341, 350), (358, 367), (375, 389), (397, 406), (414, 424), (432, 441), (449, 459), (467, 479), (487, 502), (510, 521), (529, 540), (548, 556), (564, 575), (583, 596), (604, 618), (626, 640), (648, 662), (670, 684), (692, 703), (711, 720), (728, 740), (748, 756), (764, 772), (780, 791), (799, 809), (817, 829), (837, 849), (857, 865), (870, 880), (888, 899), (907, 912), (920, 930), (938, 943), (951, 957), (965, 977), (985, 990), (998, 1007), (1015, 1023), (1031, 1041), (1049, 1056), (1064, 1070), (1078, 1084), (1092, 1101), (1109, 1123), (1131, 1141), (1149, 1162), (1170, 1180), (1188, 1199), (1207, 1217), (1225, 1246), (1254, 1264), (1272, 1283), (1291, 1301), (1309, 1319), (1327, 1339), (1347, 1361), (1369, 1382), (1390, 1405), (1413, 1428), (1436, 1451), (1459, 1471), (1479, 1484), (1489, 1499), (1507, 1513), (1521, 1529), (1534, 1541), (1549, 1566), (1574, 1585), (1593, 1606), (1614, 1627), (1635, 1650), (1658, 1674), (1682, 1700), (1708, 1724), (1732, 1748), (1756, 1769), (1777, 1787), (1795, 1805), (1813, 1822), (1830, 1842), (1850, 1855), (1863, 1871), (1879, 1885), (1893, 1903), (1911, 1918), (1926, 1936), (1944, 1951), (1959, 1973), (1981, 1991), (1999, 2013), (2021, 2035), (2043, 2054), (2062, 2072), (2080, 2085), (2093, 2098), (2106, 2111), (2119, 2130), (2138, 2145), (2153, 2167), (2175, 2179), (2184, 2194), (2202, 2212), (2220, 2232), (2240, 2247), (2255, 2266), (2274, 2283), (2291, 2300), (2308, 2315), (2323, 2330), (2338, 2346), (2354, 2364), (2372, 2382), (2390, 2400), (2408, 2413), (2421, 2433), (2441, 2451), (2459, 2463), (2471, 2476), (2484, 2492), (2500, 2507), (2515, 2525), (2533, 2540), (2548, 2554), (2562, 2568), (2573, 2584), (2592, 2599), (2604, 2616)]; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn table_is_sorted_and_complete() { + assert_eq!(named_color("aliceblue"), Some("#f0f8ff")); + assert_eq!(named_color("rebeccapurple"), Some("#663399")); + assert_eq!(named_color("yellow"), Some("#ff0")); + assert_eq!(named_color("black"), Some("#000")); + assert_eq!(named_color("teal"), Some("#008080")); + assert_eq!(named_color("missing"), None); + assert_eq!(named_color("zzz"), None); + assert_eq!(named_color("a"), None); + assert_eq!(OFFSETS.len(), 148); + for &(name_off, hex_off) in OFFSETS { + let hex = &BLOB[hex_off as usize..]; + assert!(hex.starts_with('#'), "{}", &BLOB[name_off as usize..hex_off as usize]); + } + } +} diff --git a/experiments/css/crates/css-compiler/src/conditions.rs b/experiments/css/crates/css-compiler/src/conditions.rs new file mode 100644 index 0000000..64ab600 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/conditions.rs @@ -0,0 +1,778 @@ +//! Parsing of raw at-rule preludes (`@media`, `@container`, `@supports`) +//! into the typed IR condition trees consumed by the runtime. +//! +//! The shim captures preludes as raw source text; this module re-tokenizes +//! them with `cssparser` and builds the same shapes lightningcss's typed AST +//! used to produce: legacy `min-*`/`max-*` names canonicalize into ranges, +//! interval comparisons stay as-written (the feature sits on the right of +//! the first operator), discrete feature values lowercase, and `@supports` +//! leaves are pre-evaluated at compile time. + +use bombshell_css_core::{ + Compare, ContainerCondition, Feature, FeatureKind, FeatureValue, MediaCondition, + SupportsCondition, +}; +use bombshell_css_core::tokens::write_token; +use cssparser::{Parser as CssParser, ParserInput, Token}; + +use bombshell_css_core::selectors::validate_and_specificity; +use crate::known_properties::is_known_property; + +// --------------------------------------------------------------------------- +// @media +// --------------------------------------------------------------------------- + +/// Parse a whole `@media` prelude. Comma-separated queries are ORs. +pub(crate) fn parse_media_prelude(text: &str) -> MediaCondition { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + + let mut branches: Vec = Vec::new(); + let mut current: Vec = Vec::new(); + + loop { + match parser.next_including_whitespace() { + Ok(Token::Comma) => { + if !current.is_empty() { + branches.push(and_all(std::mem::take(&mut current))); + } + } + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + // A leading group is its own query (`(a) and (b)`, comma lists): + // dispatch it directly, then let the query parser read the rest. + Ok(Token::ParenthesisBlock) => { + current.push(parse_media_in_parens(&mut parser)); + } + Ok(_) => match parse_media_query(&mut parser) { + Some(condition) => current.push(condition), + None => current.push(MediaCondition::Unknown), + }, + Err(_) => break, + } + } + + if !current.is_empty() { + branches.push(and_all(current)); + } + + match branches.len() { + 0 => MediaCondition::And(vec![]), + 1 => branches.into_iter().next().unwrap(), + _ => MediaCondition::Or(branches), + } +} + +/// One comma-separated media query: `[only | not]? type? [and group]*`. +fn parse_media_query(parser: &mut CssParser<'_, '_>) -> Option { + let mut conjuncts: Vec = Vec::new(); + let mut qualifier_not = false; + + loop { + match parser.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Comma) => break, + Ok(Token::Ident(name)) => { + let name = name.to_ascii_lowercase(); + match name.as_str() { + "only" => {} + "not" if conjuncts.is_empty() => qualifier_not = true, + "and" | "or" => {} + type_name => { + // Media type: the terminal target is a screen-like + // device. + let matches = matches!(type_name, "all" | "screen"); + if !matches { + conjuncts + .push(MediaCondition::Not(Box::new(MediaCondition::And(vec![])))); + } + } + } + } + Ok(Token::ParenthesisBlock) => { + conjuncts.push(parse_media_in_parens(parser)); + } + Ok(_) => return None, + Err(_) => break, + } + } + + let combined = match conjuncts.len() { + 0 => MediaCondition::And(vec![]), + 1 => conjuncts.into_iter().next().unwrap(), + _ => MediaCondition::And(conjuncts), + }; + Some(if qualifier_not { + MediaCondition::Not(Box::new(combined)) + } else { + combined + }) +} + +/// The contents of one `(...)` media group: either a single feature or +/// nested `and`/`or`/`not` algebra. +fn parse_media_in_parens(parser: &mut CssParser<'_, '_>) -> MediaCondition { + let text = capture_nested_text(parser); + + if let Some(feature) = parse_feature_text(&text) { + return MediaCondition::Feature(feature); + } + + let mut input = ParserInput::new(&text); + let mut inner = CssParser::new(&mut input); + + let mut operands: Vec = Vec::new(); + let mut operator: Option<&'static str> = None; + let mut negate = false; + + loop { + match inner.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Ident(name)) => { + let name = name.to_ascii_lowercase(); + match name.as_str() { + "not" => negate = true, + "and" | "or" => { + let op: &'static str = if name == "and" { "and" } else { "or" }; + match operator { + None => operator = Some(op), + Some(existing) if existing == op => {} + // Mixed and/or at one level is invalid. + Some(_) => return MediaCondition::Unknown, + } + } + _ => return MediaCondition::Unknown, + } + } + Ok(Token::ParenthesisBlock) => { + operands.push(parse_media_in_parens(&mut inner)); + } + Ok(_) => return MediaCondition::Unknown, + Err(_) => break, + } + } + + let combined = match operands.len() { + 0 => MediaCondition::And(vec![]), + 1 => operands.into_iter().next().unwrap(), + _ => match operator { + Some("or") => MediaCondition::Or(operands), + _ => MediaCondition::And(operands), + }, + }; + + if negate { + MediaCondition::Not(Box::new(combined)) + } else { + combined + } +} + +// --------------------------------------------------------------------------- +// @container +// --------------------------------------------------------------------------- + +/// Parse a `@container` prelude: `[container-name]? condition`. +pub(crate) fn parse_container_prelude(text: &str) -> (Option, ContainerCondition) { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + + let mut name: Option = None; + + loop { + match parser.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Ident(ident)) => name = Some(ident.to_string()), + Ok(Token::ParenthesisBlock) => { + return (name, parse_container_in_parens(&mut parser)); + } + Ok(Token::Function(fn_name)) => { + let lower = fn_name.to_ascii_lowercase(); + let condition = match lower.as_str() { + "style" => ContainerCondition::Style { + text: capture_nested_text(&mut parser), + }, + "scroll-state" => { + consume_nested(&mut parser); + ContainerCondition::Unknown + } + "not" => { + ContainerCondition::Not(Box::new(parse_container_in_parens(&mut parser))) + } + _ => ContainerCondition::Unknown, + }; + return (name, condition); + } + Ok(_) | Err(_) => return (name, ContainerCondition::And(vec![])), + } + } +} + +/// The contents of one `(...)` container group: a size feature, or nested +/// `and`/`or`/`not` algebra (including `style(...)` operands). +fn parse_container_in_parens(parser: &mut CssParser<'_, '_>) -> ContainerCondition { + let text = capture_nested_text(parser); + + if let Some(feature) = parse_feature_text(&text) { + return ContainerCondition::Size(feature); + } + + let mut input = ParserInput::new(&text); + let mut inner = CssParser::new(&mut input); + + let mut operands: Vec = Vec::new(); + let mut operator: Option<&'static str> = None; + let mut negate = false; + + loop { + match inner.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Ident(name)) => { + let name = name.to_ascii_lowercase(); + match name.as_str() { + "not" => negate = true, + "and" | "or" => { + let op: &'static str = if name == "and" { "and" } else { "or" }; + match operator { + None => operator = Some(op), + Some(existing) if existing == op => {} + Some(_) => return ContainerCondition::Unknown, + } + } + _ => return ContainerCondition::Unknown, + } + } + Ok(Token::ParenthesisBlock) => { + operands.push(parse_container_in_parens(&mut inner)); + } + Ok(Token::Function(fn_name)) => { + let lower = fn_name.to_ascii_lowercase(); + match lower.as_str() { + "style" => operands.push(ContainerCondition::Style { + text: capture_nested_text(&mut inner), + }), + "scroll-state" => { + consume_nested(&mut inner); + operands.push(ContainerCondition::Unknown); + } + "not" => { + let inner_condition = parse_container_in_parens(&mut inner); + operands.push(ContainerCondition::Not(Box::new(inner_condition))); + } + _ => return ContainerCondition::Unknown, + } + } + Ok(_) => return ContainerCondition::Unknown, + Err(_) => break, + } + } + + let combined = match operands.len() { + 0 => ContainerCondition::And(vec![]), + 1 => operands.into_iter().next().unwrap(), + _ => match operator { + Some("or") => ContainerCondition::Or(operands), + _ => ContainerCondition::And(operands), + }, + }; + + if negate { + ContainerCondition::Not(Box::new(combined)) + } else { + combined + } +} + +// --------------------------------------------------------------------------- +// @supports +// --------------------------------------------------------------------------- + +/// Parse a `@supports` prelude. +pub(crate) fn parse_supports_prelude(text: &str) -> SupportsCondition { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + + parse_supports_algebra(&mut parser) +} + +/// Boolean algebra at one level over supports operands. +fn parse_supports_algebra(parser: &mut CssParser<'_, '_>) -> SupportsCondition { + let mut operands: Vec = Vec::new(); + let mut operator: Option<&'static str> = None; + let mut negate = false; + + loop { + match parser.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Ident(name)) => { + let name = name.to_ascii_lowercase(); + match name.as_str() { + "not" => negate = true, + "and" | "or" => { + let op: &'static str = if name == "and" { "and" } else { "or" }; + match operator { + None => operator = Some(op), + Some(existing) if existing == op => {} + Some(_) => return SupportsCondition::Unknown, + } + } + _ => return SupportsCondition::Unknown, + } + } + Ok(Token::ParenthesisBlock) => { + operands.push(parse_supports_in_parens(parser)); + } + Ok(Token::Function(fn_name)) => { + let lower = fn_name.to_ascii_lowercase(); + match lower.as_str() { + "selector" => { + let inner_text = capture_nested_text(parser); + let supported = + validate_and_specificity(inner_text.trim()).is_ok(); + operands.push(SupportsCondition::Selector { + text: inner_text, + supported, + }); + } + _ => return SupportsCondition::Unknown, + } + } + Ok(_) => return SupportsCondition::Unknown, + Err(_) => break, + } + } + + let combined = match operands.len() { + 0 => SupportsCondition::And(vec![]), + 1 => operands.into_iter().next().unwrap(), + _ => match operator { + Some("or") => SupportsCondition::Or(operands), + _ => SupportsCondition::And(operands), + }, + }; + + if negate { + SupportsCondition::Not(Box::new(combined)) + } else { + combined + } +} + +/// One `(...)` supports group: either a declaration test or nested algebra. +fn parse_supports_in_parens(parser: &mut CssParser<'_, '_>) -> SupportsCondition { + let text = capture_nested_text(parser); + let trimmed = text.trim(); + + // Declaration form: `name: value`. + let mut input = ParserInput::new(trimmed); + let mut inner = CssParser::new(&mut input); + let is_declaration = inner + .try_parse(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + match parser.next() { + Ok(Token::Ident(_)) => {} + Ok(_) => return Err(custom_err(parser)), + Err(_) => return Err(custom_err(parser)), + } + match parser.next() { + Ok(Token::Colon) => {} + Ok(_) => return Err(custom_err(parser)), + Err(_) => return Err(custom_err(parser)), + } + Ok(()) + }) + .is_ok(); + + if is_declaration { + if let Some((name, value)) = trimmed.split_once(':') { + let value = value.trim().to_string(); + let name = name.trim().to_ascii_lowercase(); + let supported = is_known_property(&name) || name.starts_with("--"); + return SupportsCondition::Declaration { + name, + value, + supported, + }; + } + } + + // Nested algebra. + let mut input = ParserInput::new(trimmed); + let mut inner = CssParser::new(&mut input); + parse_supports_algebra(&mut inner) +} + +// --------------------------------------------------------------------------- +// Features +// --------------------------------------------------------------------------- + +/// Try to read one captured `(...)` group as a media/container size feature. +fn parse_feature_text(text: &str) -> Option { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + + parser.try_parse(parse_feature_block).ok() +} + +fn parse_feature_block<'i, 'tt>( + parser: &mut CssParser<'i, 'tt>, +) -> Result> { + let pieces = collect_feature_pieces(parser)?; + build_feature(pieces).map_err(|()| custom_err(parser)) +} + +#[derive(Debug, Clone)] +enum Piece { + Name(String), + Op(Compare), + Colon, + Value(FeatureValue), +} + +fn collect_feature_pieces<'i, 'tt>( + parser: &mut CssParser<'i, 'tt>, +) -> Result, cssparser::ParseError<'i, ()>> { + let mut pieces: Vec = Vec::new(); + let mut name_seen = false; + + // The captured group text is the inner content: read to the end. + loop { + match parser.next_including_whitespace() { + Ok(Token::Comment(_) | Token::WhiteSpace(_)) => {} + Ok(Token::Ident(name)) => { + // The feature name comes first; later identifiers are + // discrete values (`(prefers-color-scheme: dark)`). + if name_seen { + pieces.push(Piece::Value(FeatureValue::Ident(name.to_ascii_lowercase()))); + } else { + pieces.push(Piece::Name(name.to_string())); + name_seen = true; + } + } + Ok(Token::Delim(op @ ('<' | '>' | '='))) => { + let mut cmp = match op { + '<' => Compare::LessThan, + '>' => Compare::GreaterThan, + _ => Compare::Equal, + }; + // Optional `=`. + if let Ok(Token::Delim('=')) = parser.next_including_whitespace() { + if cmp != Compare::Equal { + cmp = match cmp { + Compare::LessThan => Compare::LessThanEqual, + Compare::GreaterThan => Compare::GreaterThanEqual, + _ => cmp, + }; + } + } + pieces.push(Piece::Op(cmp)); + } + Ok(Token::Colon) => pieces.push(Piece::Colon), + Ok( + token @ (Token::Number { .. } | Token::Percentage { .. } | Token::Dimension { .. }), + ) => { + pieces.push(Piece::Value(media_value(token))); + } + Ok(_) => return Err(custom_err(parser)), + Err(_) => break, + } + } + + Ok(pieces) +} + +/// Strictly validate the piece sequence into the IR feature shape. +fn build_feature(pieces: Vec) -> Result { + let names: Vec = pieces + .iter() + .enumerate() + .filter(|(_, p)| matches!(p, Piece::Name(_))) + .map(|(i, _)| i) + .collect(); + let ops: Vec = pieces + .iter() + .enumerate() + .filter(|(_, p)| matches!(p, Piece::Op(_))) + .map(|(i, _)| i) + .collect(); + + match (names.len(), ops.len()) { + // Boolean presence test: `(hover)`. + (1, 0) if pieces.len() == 1 => Ok(Feature { + name: canonical_name(&pieces, names[0]), + kind: FeatureKind::Boolean, + }), + // Plain with colon: `(min-width: 24em)`, `(prefers-color-scheme: dark)`, + // `(aspect-ratio: 16/9)`. + (1, 0) if pieces.len() >= 3 && matches!(&pieces[1], Piece::Colon) => { + let value = take_value(&pieces[2..]).ok_or(())?; + normalize(Feature { + name: canonical_name(&pieces, names[0]), + kind: FeatureKind::Plain { value }, + }) + } + // Range with the feature on the left: `(width > 400px)`. + (1, 1) if names[0] == 0 && ops[0] == 1 && pieces.len() == 3 => { + let Piece::Op(cmp) = pieces[ops[0]].clone() else { + return Err(()); + }; + let value = take_value(&pieces[2..]).ok_or(())?; + normalize(Feature { + name: canonical_name(&pieces, names[0]), + kind: FeatureKind::Range { cmp, value }, + }) + } + // Range with the feature on the right: `(400px < width)`. + (1, 1) if ops[0] == 1 && pieces.len() == 3 => { + let Piece::Op(cmp) = pieces[ops[0]].clone() else { + return Err(()); + }; + let value = take_value(&pieces[..1]).ok_or(())?; + normalize(Feature { + name: canonical_name(&pieces, names[0]), + kind: FeatureKind::Range { + cmp: mirror(cmp), + value, + }, + }) + } + // Interval: `(400px < width < 800px)`. Comparisons stay as-written; + // the runtime evaluates `start op dimension`, then `dimension op end`. + (1, 2) if ops[0] == 1 && names[0] == 2 && ops[1] == 3 && pieces.len() == 5 => { + let (Piece::Op(start_cmp), Piece::Op(end_cmp)) = + (pieces[ops[0]].clone(), pieces[ops[1]].clone()) + else { + return Err(()); + }; + let start = take_value(&pieces[..1]).ok_or(())?; + let end = take_value(&pieces[4..]).ok_or(())?; + Ok(Feature { + name: canonical_name(&pieces, names[0]), + kind: FeatureKind::Interval { + start, + start_cmp, + end, + end_cmp, + }, + }) + } + _ => Err(()), + } +} + +/// Consume one value (with an optional ratio slash) from the pieces. +fn take_value(pieces: &[Piece]) -> Option { + let mut iter = pieces.iter(); + let first = match iter.next()? { + Piece::Value(v) => v.clone(), + _ => return None, + }; + + match iter.next() { + None => Some(first), + Some(Piece::Value(second)) => ratio_of(first, second.clone()), + Some(_) => None, + } +} + +fn ratio_of(first: FeatureValue, second: FeatureValue) -> Option { + let numerator = match first { + FeatureValue::Integer(a) => a as f32, + FeatureValue::Number(a) => a, + _ => return None, + }; + let denominator = match second { + FeatureValue::Integer(b) => b as f32, + FeatureValue::Number(b) => b, + _ => return None, + }; + Some(FeatureValue::Ratio(numerator, denominator)) +} + +fn canonical_name(pieces: &[Piece], index: usize) -> String { + match &pieces[index] { + Piece::Name(name) => name.to_ascii_lowercase(), + _ => String::new(), + } +} + +/// Legacy `min-*`/`max-*` names become equivalent range shapes. +fn normalize(feature: Feature) -> Result { + if let FeatureKind::Plain { value } = &feature.kind { + if let Some(base) = feature.name.strip_prefix("min-") { + return Ok(Feature { + name: base.to_string(), + kind: FeatureKind::Range { + cmp: Compare::GreaterThanEqual, + value: value.clone(), + }, + }); + } + if let Some(base) = feature.name.strip_prefix("max-") { + return Ok(Feature { + name: base.to_string(), + kind: FeatureKind::Range { + cmp: Compare::LessThanEqual, + value: value.clone(), + }, + }); + } + } + Ok(feature) +} + +fn mirror(cmp: Compare) -> Compare { + match cmp { + Compare::LessThan => Compare::GreaterThan, + Compare::LessThanEqual => Compare::GreaterThanEqual, + Compare::GreaterThan => Compare::LessThan, + Compare::GreaterThanEqual => Compare::LessThanEqual, + Compare::Equal => Compare::Equal, + } +} + +/// Convert a value token into the IR feature value. +fn media_value(token: &Token<'_>) -> FeatureValue { + match token { + Token::Number { + value, int_value, .. + } => match int_value { + Some(int) => FeatureValue::Integer(*int), + None => FeatureValue::Number(*value), + }, + Token::Percentage { .. } => FeatureValue::Unknown, + Token::Dimension { value, unit, .. } => { + let unit_lower = unit.to_ascii_lowercase(); + match unit_lower.as_str() { + "px" => FeatureValue::Px(*value), + "dpi" | "dpcm" | "dppx" | "x" => { + let v = *value; + FeatureValue::Dppx(match unit_lower.as_str() { + "dpi" => v / 96.0, + "dpcm" => v * 2.54 / 96.0, + _ => v, + }) + } + other => FeatureValue::RelativeLength { + value: *value, + unit: other.to_string(), + }, + } + } + Token::Ident(ident) => FeatureValue::Ident(ident.to_ascii_lowercase()), + _ => FeatureValue::Unknown, + } +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +/// Consume the current nested block and return its text, recursing into +/// nested blocks so nothing is lost to the block-skipping tokenizer. +fn capture_nested_text(parser: &mut CssParser<'_, '_>) -> String { + let mut out = String::new(); + let _ = parser.parse_nested_block(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + capture_tokens(parser, &mut out); + Ok(()) + }); + out +} + +fn capture_tokens(parser: &mut CssParser<'_, '_>, out: &mut String) { + loop { + match parser.next_including_whitespace() { + Ok(Token::Comment(_)) => {} + Ok(Token::WhiteSpace(_)) => { + if !out.is_empty() { + out.push(' '); + } + } + Ok(Token::Function(name)) => { + out.push_str(name); + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + capture_tokens(parser, out); + Ok(()) + }, + ); + out.push(')'); + } + Ok(Token::ParenthesisBlock) => { + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + capture_tokens(parser, out); + Ok(()) + }, + ); + out.push(')'); + } + Ok(Token::SquareBracketBlock) => { + out.push('['); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + capture_tokens(parser, out); + Ok(()) + }, + ); + out.push(']'); + } + Ok(Token::CurlyBracketBlock) => { + out.push('{'); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + capture_tokens(parser, out); + Ok(()) + }, + ); + out.push('}'); + } + Ok(token) => write_token(out, &token), + Err(_) => break, + } + } +} + +fn consume_nested(parser: &mut CssParser<'_, '_>) { + let _ = parser + .parse_nested_block(|_parser| -> Result<(), cssparser::ParseError<'_, ()>> { Ok(()) }); +} + +fn and_all(conditions: Vec) -> MediaCondition { + match conditions.len() { + 0 => MediaCondition::And(vec![]), + 1 => conditions.into_iter().next().unwrap(), + _ => MediaCondition::And(conditions), + } +} + +fn custom_err<'i>(parser: &CssParser<'i, '_>) -> cssparser::ParseError<'i, ()> { + cssparser::ParseError { + kind: cssparser::ParseErrorKind::Custom(()), + location: parser.current_source_location(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_plain_feature() { + let c = parse_media_prelude("(min-width: 24em)"); + let MediaCondition::Feature(f) = c else { + panic!("expected feature, got {c:?}"); + }; + assert_eq!(f.name, "width"); + } + + #[test] + fn parses_not_hover() { + let c = parse_media_prelude("screen and (not (hover))"); + let MediaCondition::Not(inner) = c else { + panic!("expected a negated condition, got {c:?}"); + }; + // `(hover)` is a single feature; the group collapses to it. + assert!(matches!(*inner, MediaCondition::Feature(_))); + } +} diff --git a/experiments/css/crates/css-compiler/src/expand.rs b/experiments/css/crates/css-compiler/src/expand.rs new file mode 100644 index 0000000..617f7f7 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/expand.rs @@ -0,0 +1,338 @@ +//! Nesting expansion and selector normalization. +//! +//! Style-rule preludes arrive from the shim as raw source text. The compiler +//! tokenizes them with `cssparser`, rewrites every `&` (nesting selector) +//! into the parent selector — a single parent inlines directly +//! (browser-canonical, keeps specificity exact), a parent list wraps in +//! `:is()` — and re-serializes with canonical whitespace: combinators lose +//! their surrounding spaces, descendant relationships keep exactly one space +//! when the source had one, and `nth-*` arguments keep only the `of` clause +//! spacing. + +use bombshell_css_core::tokens::write_token; +use cssparser::{Parser as CssParser, ParserInput, Token}; + +/// Expand a raw selector prelude against the enclosing parent selectors. +/// +/// Every `&` token (including inside functional arguments such as `:is(&, .b)` +/// and `:has(&)`) is replaced by the parent selectors. A nested selector +/// without an explicit `&` gets an implicit descendant prefix. +/// +/// `parents` are already-expanded parent selector texts (one complex selector +/// each). With no parents (top level) the text is only normalized. +pub(crate) fn expand_selector_list(text: &str, parents: &[String]) -> String { + let mut s = Serializer { + parents, + out: String::with_capacity(text.len() + 16), + segments: Vec::new(), + depth: 0, + bracket_depth: 0, + closed: false, + gap: false, + first: true, + saw_nesting: false, + after_of: false, + }; + + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + + loop { + match parser.next_including_whitespace_and_comments() { + Ok(token) => match token { + Token::Comment(_) => {} + Token::WhiteSpace(_) => s.gap = true, + Token::Comma => s.comma(), + Token::Delim('&') => s.substitute(), + Token::Delim('*') => s.universal(), + Token::Delim(d @ ('>' | '+' | '~')) => s.combinator(*d), + Token::Delim(d @ ('.' | '#')) => s.attach(*d), + Token::Delim(d) => s.other_delim(*d), + Token::Colon => s.colon(), + Token::Ident(_) | Token::Hash(_) => s.simple(token), + Token::Function(name) => { + s.space(); + s.out.push_str(name); + s.out.push('('); + s.enter_block(false, &mut parser); + s.out.push(')'); + s.closed = true; + s.gap = false; + } + Token::ParenthesisBlock => { + s.space(); + s.out.push('('); + s.enter_block(false, &mut parser); + s.out.push(')'); + s.closed = true; + s.gap = false; + } + Token::SquareBracketBlock => { + s.space(); + s.out.push('['); + s.enter_block(true, &mut parser); + s.out.push(']'); + s.closed = true; + s.gap = false; + } + other => s.other(other), + }, + Err(_) => break, + } + } + + s.finish_segment(); + + let joined = if s.segments.len() == 1 { + s.segments.pop().unwrap_or_default() + } else { + s.segments.join(",") + }; + + canonicalize_nth(&joined) +} + +fn substitution_text(parents: &[String]) -> String { + if parents.len() == 1 { + parents[0].clone() + } else { + { + let mut out = String::from(":is("); + for (i, parent) in parents.iter().enumerate() { + if i > 0 { + out.push_str(", "); + } + out.push_str(parent); + } + out.push(')'); + out + } + } +} + +struct Serializer<'a> { + parents: &'a [String], + out: String, + segments: Vec, + depth: usize, + bracket_depth: usize, + closed: bool, + gap: bool, + first: bool, + saw_nesting: bool, + after_of: bool, +} + +impl<'a> Serializer<'a> { + fn comma(&mut self) { + self.finish_segment(); + self.closed = false; + self.gap = false; + self.first = true; + self.saw_nesting = false; + } + + fn universal(&mut self) { + self.space(); + self.out.push('*'); + self.closed = true; + self.gap = false; + self.first = false; + } + + fn combinator(&mut self, d: char) { + self.out.push(d); + self.closed = false; + self.gap = false; + self.first = false; + } + + fn attach(&mut self, d: char) { + self.space(); + self.out.push(d); + self.closed = false; + self.gap = false; + self.first = false; + } + + fn other_delim(&mut self, d: char) { + self.space(); + self.out.push(d); + self.closed = true; + self.gap = false; + self.first = false; + } + + fn colon(&mut self) { + self.space(); + self.out.push(':'); + self.closed = false; + self.gap = false; + self.first = false; + } + + fn simple<'i>(&mut self, token: &Token<'i>) { + self.space(); + write_token(&mut self.out, token); + self.closed = true; + self.gap = false; + self.first = false; + } + + fn other<'i>(&mut self, token: &Token<'i>) { + self.space(); + write_token(&mut self.out, token); + self.closed = true; + self.gap = false; + self.first = false; + } + + fn enter_block(&mut self, bracket: bool, parser: &mut CssParser<'_, '_>) { + if bracket { + self.bracket_depth += 1; + } else { + self.depth += 1; + } + self.gap = false; + self.after_of = false; + self.first = false; + self.descend(parser); + if bracket { + self.bracket_depth -= 1; + } else { + self.depth -= 1; + } + } + + /// Emit one space when source whitespace separated a completed compound + /// from a new one (a descendant relationship). + fn space(&mut self) { + if self.depth == 0 && self.bracket_depth == 0 && self.gap && !self.first && self.closed { + self.out.push(' '); + } + self.gap = false; + } + + fn substitute(&mut self) { + if self.depth == 0 && self.bracket_depth == 0 { + self.space(); + } else if self.gap && self.after_of { + self.out.push(' '); + } + self.gap = false; + self.after_of = false; + self.out.push_str(&substitution_text(self.parents)); + self.closed = true; + self.gap = false; + self.first = false; + self.saw_nesting = true; + } + + /// Serialize the tokens inside a nested block (function arguments, + /// attribute blocks), replacing `&`. Argument blocks keep only the + /// spacing around the An+B `of` clause; attribute blocks keep single + /// spaces verbatim. + fn descend(&mut self, parser: &mut CssParser<'_, '_>) { + let _ = parser.parse_nested_block(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + loop { + match parser.next_including_whitespace_and_comments() { + Ok(token) => match token { + Token::Comment(_) => {} + Token::WhiteSpace(_) => { + if self.bracket_depth > 0 && !self.out.ends_with(' ') { + self.out.push(' '); + } else { + self.gap = true; + } + } + Token::Delim('&') => { + self.substitute(); + } + token => { + // Inside argument blocks whitespace is + // insignificant, except around the An+B `of` + // keyword. + let is_of = matches!(token, Token::Ident(name) if name.eq_ignore_ascii_case("of")); + if self.gap && (self.after_of || is_of) { + self.out.push(' '); + } + self.gap = false; + write_token(&mut self.out, token); + self.after_of = is_of; + } + }, + Err(_) => break, + } + } + Ok(()) + }); + } + + fn finish_segment(&mut self) { + let segment = std::mem::take(&mut self.out); + let segment = segment.trim(); + + if !self.saw_nesting && !self.parents.is_empty() { + self.segments + .push({ + let mut out = substitution_text(self.parents); + out.push(' '); + out.push_str(&segment); + out + }); + } else { + self.segments.push(segment.to_string()); + } + } +} + +/// `2n+1` → `odd`, `2n` → `even` inside `nth-*` arguments. +fn canonicalize_nth(text: &str) -> String { + let mut out = String::with_capacity(text.len()); + let mut rest = text; + + while let Some(index) = rest.find("nth-") { + let (before, after) = rest.split_at(index); + out.push_str(before); + rest = after; + + let Some(open) = rest.find('(') else { + out.push_str(rest); + return out; + }; + out.push_str(&rest[..=open]); + rest = &rest[open + 1..]; + + // Canonicalize the leading An+B term of the argument. + if let Some(tail) = rest.strip_prefix("2n+1") { + out.push_str("odd"); + rest = tail; + } else if let Some(tail) = rest.strip_prefix("2n") { + if matches!(tail.as_bytes().first(), Some(b')' | b',' | b' ')) { + out.push_str("even"); + rest = tail; + } + } + + // Copy until the argument ends (or the next `nth-` may start). + let mut consumed = 0usize; + let mut depth = 0usize; + for (i, c) in rest.char_indices() { + match c { + '(' => depth += 1, + ')' if depth == 0 => { + consumed = i; + break; + } + ')' => depth -= 1, + _ => {} + } + consumed = i + c.len_utf8(); + } + out.push_str(&rest[..consumed]); + rest = &rest[consumed..]; + } + + out.push_str(rest); + out +} diff --git a/experiments/css/crates/css-compiler/src/ffi.rs b/experiments/css/crates/css-compiler/src/ffi.rs new file mode 100644 index 0000000..0f79495 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/ffi.rs @@ -0,0 +1,83 @@ +//! FFI boundary to the lexbor shim (`c/shim.c`). + +use std::os::raw::c_int; +use std::sync::{Mutex, MutexGuard, OnceLock}; + +/// Event kinds emitted by the shim. Spans are byte offsets into the original +/// stylesheet text. +pub const STYLE_BEGIN: u8 = 0; +pub const STYLE_END: u8 = 1; +pub const AT_BEGIN: u8 = 2; +pub const AT_PRELUDE: u8 = 3; +pub const AT_END: u8 = 4; +pub const DECL: u8 = 5; + +pub const FLAG_IMPORTANT: u8 = 0x01; +pub const FLAG_FAILED: u8 = 0x02; + +/// One shim event. `a`/`b` are `[start, end)` byte ranges into the source. +#[repr(C)] +#[derive(Clone, Copy)] +pub struct LbxEvent { + pub kind: u8, + pub flags: u8, + pub a0: u32, + pub a1: u32, + pub b0: u32, + pub b1: u32, +} + +impl LbxEvent { + pub fn span_a<'t>(&self, text: &'t str) -> &'t str { + &text[self.a0 as usize..self.a1 as usize] + } + + pub fn span_b<'t>(&self, text: &'t str) -> &'t str { + &text[self.b0 as usize..self.b1 as usize] + } +} + +unsafe extern "C" { + /// Parse a stylesheet into the event stream. Returns 0 on success. The + /// event array is allocated by the shim and must be released with + /// `lbx_free_events`. + fn lbx_parse( + css: *const u8, + css_len: usize, + out_events: *mut *mut LbxEvent, + out_len: *mut usize, + ) -> c_int; + + fn lbx_free_events(events: *mut LbxEvent, len: usize); +} + +/// The shim keeps one global context per process; guard the C boundary. +fn shim_lock() -> MutexGuard<'static, ()> { + static LOCK: OnceLock> = OnceLock::new(); + let _ = LOCK.get_or_init(|| Mutex::new(())); + LOCK.get() + .unwrap() + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +/// Parse a stylesheet into the shim event stream. +pub(crate) fn parse_events(css: &str) -> Option> { + let _guard = shim_lock(); + + let bytes = css.as_bytes(); + let mut events: *mut LbxEvent = std::ptr::null_mut(); + let mut len: usize = 0; + + let status = unsafe { lbx_parse(bytes.as_ptr(), bytes.len(), &mut events, &mut len) }; + + if status != 0 { + return None; + } + + let slice = unsafe { std::slice::from_raw_parts(events, len) }; + let owned = slice.to_vec(); + unsafe { lbx_free_events(events, len) }; + + Some(owned) +} diff --git a/experiments/css/crates/css-compiler/src/known_properties.rs b/experiments/css/crates/css-compiler/src/known_properties.rs new file mode 100644 index 0000000..75dc756 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/known_properties.rs @@ -0,0 +1,43 @@ +//! Property-name knowledge for `@supports` pre-evaluation. +//! +//! lightningcss's typed parser decided `@supports (prop: value)` leaves by +//! whether it recognized the property. This list carries the same job: it is +//! a curated set of properties the runtime understands or that are broadly +//! implemented in modern engines. Unknown names report unsupported, matching +//! browser behavior. + +/// Whether `@supports (name: value)` should report supported at compile time. +pub(crate) fn is_known_property(name: &str) -> bool { + // Packed storage: one NUL-joined blob plus u16 entry offsets, searched + // like the sorted pointer-array table it replaced (same compares, + // smaller footprint in linear memory). + OFFSETS + .binary_search_by(|&off| { + let rest = &BLOB[off as usize..]; + let end = rest.find('\0').unwrap_or(rest.len()); + rest[..end].cmp(name) + }) + .is_ok() +} + +const BLOB: &str = "align-content\0align-items\0align-self\0all\0appearance\0aspect-ratio\0backdrop-filter\0background\0background-attachment\0background-blend-mode\0background-clip\0background-color\0background-image\0background-origin\0background-position\0background-repeat\0background-size\0block-size\0border\0border-block\0border-block-color\0border-block-end\0border-block-end-color\0border-block-end-style\0border-block-end-width\0border-block-start\0border-block-start-color\0border-block-start-style\0border-block-start-width\0border-block-style\0border-block-width\0border-bottom\0border-bottom-color\0border-bottom-left-radius\0border-bottom-right-radius\0border-bottom-style\0border-bottom-width\0border-collapse\0border-color\0border-end-end-radius\0border-end-start-radius\0border-image\0border-image-outset\0border-image-repeat\0border-image-slice\0border-image-source\0border-image-width\0border-inline\0border-inline-color\0border-inline-end\0border-inline-end-color\0border-inline-end-style\0border-inline-end-width\0border-inline-start\0border-inline-start-color\0border-inline-start-style\0border-inline-start-width\0border-inline-style\0border-inline-width\0border-left\0border-left-color\0border-left-style\0border-left-width\0border-radius\0border-right\0border-right-color\0border-right-style\0border-right-width\0border-spacing\0border-start-end-radius\0border-start-start-radius\0border-style\0border-top\0border-top-color\0border-top-left-radius\0border-top-right-radius\0border-top-style\0border-top-width\0border-width\0bottom\0box-decoration-break\0box-shadow\0box-sizing\0break-after\0break-before\0break-inside\0caption-side\0caret-color\0clear\0clip-path\0color\0color-interpolation-method\0color-scheme\0column-count\0column-fill\0column-gap\0column-rule\0column-rule-color\0column-rule-style\0column-rule-width\0column-span\0column-width\0columns\0contain\0container\0container-name\0container-position\0container-type\0content\0cursor\0direction\0display\0empty-cells\0filter\0flex\0flex-basis\0flex-direction\0flex-flow\0flex-grow\0flex-shrink\0flex-wrap\0float\0font\0font-family\0font-feature-settings\0font-kerning\0font-language-override\0font-optical-sizing\0font-size\0font-size-adjust\0font-stretch\0font-style\0font-synthesis\0font-variant\0font-variant-caps\0font-variant-east-asian\0font-variant-ligatures\0font-variant-numeric\0font-variant-position\0font-variation-settings\0font-weight\0forced-color-adjust\0gap\0grid\0grid-area\0grid-auto-columns\0grid-auto-flow\0grid-auto-rows\0grid-column\0grid-column-end\0grid-column-gap\0grid-column-start\0grid-gap\0grid-row\0grid-row-end\0grid-row-gap\0grid-row-start\0grid-template\0grid-template-areas\0grid-template-columns\0grid-template-rows\0height\0hyphens\0image-orientation\0image-rendering\0inline-size\0inset\0inset-block\0inset-block-end\0inset-block-start\0inset-inline\0inset-inline-end\0inset-inline-start\0isolation\0justify-content\0justify-items\0justify-self\0left\0letter-spacing\0line-break\0line-height\0list-style\0list-style-image\0list-style-position\0list-style-type\0margin\0margin-block\0margin-block-end\0margin-block-start\0margin-bottom\0margin-inline\0margin-inline-end\0margin-inline-start\0margin-left\0margin-right\0margin-top\0marker-end\0marker-mid\0marker-start\0mask\0mask-border\0mask-border-mode\0mask-border-outset\0mask-border-repeat\0mask-border-slice\0mask-border-source\0mask-border-width\0mask-clip\0mask-composite\0mask-image\0mask-mode\0mask-origin\0mask-position\0mask-repeat\0mask-size\0max-block-size\0max-height\0max-inline-size\0max-width\0min-block-size\0min-height\0min-inline-size\0min-width\0mix-blend-mode\0object-fit\0object-position\0offset\0offset-anchor\0offset-distance\0offset-path\0offset-rotate\0opacity\0order\0outline\0outline-color\0outline-offset\0outline-style\0outline-width\0overflow\0overflow-anchor\0overflow-block\0overflow-clip-margin\0overflow-inline\0overflow-wrap\0overflow-x\0overflow-y\0overscroll-behavior\0overscroll-behavior-block\0overscroll-behavior-inline\0overscroll-behavior-x\0overscroll-behavior-y\0padding\0padding-block\0padding-block-end\0padding-block-start\0padding-bottom\0padding-inline\0padding-inline-end\0padding-inline-start\0padding-left\0padding-right\0padding-top\0page-break-after\0page-break-before\0page-break-inside\0paint-order\0perspective\0perspective-origin\0place-content\0place-items\0place-self\0pointer-events\0position\0print-color-adjust\0quotes\0resize\0right\0rotate\0row-gap\0scale\0scroll-behavior\0scroll-margin\0scroll-margin-block\0scroll-margin-block-end\0scroll-margin-block-start\0scroll-margin-bottom\0scroll-margin-inline\0scroll-margin-inline-end\0scroll-margin-inline-start\0scroll-margin-left\0scroll-margin-right\0scroll-margin-top\0scroll-padding\0scroll-padding-block\0scroll-padding-block-end\0scroll-padding-block-start\0scroll-padding-bottom\0scroll-padding-inline\0scroll-padding-inline-end\0scroll-padding-inline-start\0scroll-padding-left\0scroll-padding-right\0scroll-padding-top\0scroll-snap-align\0scroll-snap-stop\0scroll-snap-type\0shape-image-threshold\0shape-margin\0shape-outside\0table-layout\0tab-size\0text-align\0text-align-all\0text-align-last\0text-combine-upright\0text-decoration\0text-decoration-color\0text-decoration-line\0text-decoration-skip-ink\0text-decoration-style\0text-decoration-thickness\0text-emphasis\0text-emphasis-color\0text-emphasis-position\0text-emphasis-style\0text-indent\0text-justify\0text-orientation\0text-overflow\0text-rendering\0text-shadow\0text-size-adjust\0text-transform\0text-underline-offset\0text-underline-position\0top\0touch-action\0transform\0transform-box\0transform-origin\0transform-style\0transition\0transition-behavior\0transition-delay\0transition-duration\0transition-property\0transition-timing-function\0translate\0unicode-bidi\0user-select\0vertical-align\0visibility\0white-space\0widows\0width\0will-change\0word-break\0word-spacing\0word-wrap\0writing-mode\0z-index\0"; + +/// Offsets of each property name into [`BLOB`], sorted; each entry runs to +/// the next offset (or the trailing NUL). +const OFFSETS: &[u16] = &[0, 14, 26, 37, 41, 52, 65, 81, 92, 114, 136, 152, 169, 186, 204, 224, 242, 258, 269, 276, 289, 308, 325, 348, 371, 394, 413, 438, 463, 488, 507, 526, 540, 560, 586, 613, 633, 653, 669, 682, 704, 728, 741, 761, 781, 800, 820, 839, 853, 873, 891, 915, 939, 963, 983, 1009, 1035, 1061, 1081, 1101, 1113, 1131, 1149, 1167, 1181, 1194, 1213, 1232, 1251, 1266, 1290, 1316, 1329, 1340, 1357, 1380, 1404, 1421, 1438, 1451, 1458, 1479, 1490, 1501, 1513, 1526, 1539, 1552, 1564, 1570, 1580, 1586, 1613, 1626, 1639, 1651, 1662, 1674, 1692, 1710, 1728, 1740, 1753, 1761, 1769, 1779, 1794, 1813, 1828, 1836, 1843, 1853, 1861, 1873, 1880, 1885, 1896, 1911, 1921, 1931, 1943, 1953, 1959, 1964, 1976, 1998, 2011, 2034, 2054, 2064, 2081, 2094, 2105, 2120, 2133, 2151, 2175, 2198, 2219, 2241, 2265, 2277, 2297, 2301, 2306, 2316, 2334, 2349, 2364, 2376, 2392, 2408, 2426, 2435, 2444, 2457, 2470, 2485, 2499, 2519, 2541, 2560, 2567, 2575, 2593, 2609, 2621, 2627, 2639, 2655, 2673, 2686, 2703, 2722, 2732, 2748, 2762, 2775, 2780, 2795, 2806, 2818, 2829, 2846, 2866, 2882, 2889, 2902, 2919, 2938, 2952, 2966, 2984, 3004, 3016, 3029, 3040, 3051, 3062, 3075, 3080, 3092, 3109, 3128, 3147, 3165, 3184, 3202, 3212, 3227, 3238, 3248, 3260, 3274, 3286, 3296, 3311, 3322, 3338, 3348, 3363, 3374, 3390, 3400, 3415, 3426, 3442, 3449, 3463, 3479, 3491, 3505, 3513, 3519, 3527, 3541, 3556, 3570, 3584, 3593, 3609, 3624, 3645, 3661, 3675, 3686, 3697, 3717, 3743, 3770, 3792, 3814, 3822, 3836, 3854, 3874, 3889, 3904, 3923, 3944, 3957, 3971, 3983, 4000, 4018, 4036, 4048, 4060, 4079, 4093, 4105, 4116, 4131, 4140, 4159, 4166, 4173, 4179, 4186, 4194, 4200, 4216, 4230, 4250, 4274, 4300, 4321, 4342, 4367, 4394, 4413, 4433, 4451, 4466, 4487, 4512, 4539, 4561, 4583, 4609, 4637, 4657, 4678, 4697, 4715, 4732, 4749, 4771, 4784, 4798, 4811, 4820, 4831, 4846, 4862, 4883, 4899, 4921, 4942, 4967, 4989, 5015, 5029, 5049, 5072, 5092, 5104, 5117, 5134, 5148, 5163, 5175, 5192, 5207, 5229, 5253, 5257, 5270, 5280, 5294, 5311, 5327, 5338, 5358, 5375, 5395, 5415, 5442, 5452, 5465, 5477, 5492, 5503, 5515, 5522, 5528, 5540, 5551, 5564, 5574, 5587]; + +#[cfg(test)] +mod tests { + use super::is_known_property; + + #[test] + fn table_is_sorted_and_complete() { + assert!(is_known_property("align-content")); + assert!(is_known_property("z-index")); + assert!(is_known_property("width")); + assert!(!is_known_property("align-contentt")); + assert!(!is_known_property("unknown-property")); + assert!(!is_known_property("")); + assert_eq!(super::OFFSETS.len(), 361); + } +} diff --git a/experiments/css/crates/css-compiler/src/lib.rs b/experiments/css/crates/css-compiler/src/lib.rs new file mode 100644 index 0000000..463c0f9 --- /dev/null +++ b/experiments/css/crates/css-compiler/src/lib.rs @@ -0,0 +1,706 @@ +//! Lexbor-backed compiler for the Bombshell CSS pipeline. +//! +//! `compile` parses full modern CSS (Syntax Level 3/4, nesting, media, +//! container, supports, layers) with lexbor's syntax parser via a thin C +//! shim, then performs all semantic work in Rust: nesting expansion against +//! the CSS Nesting spec, typed condition trees, canonicalization, and +//! emission of the versioned [`CompiledStylesheet`] IR consumed by the +//! runtime. Selectors, declaration values, and keyframes cross the boundary +//! as canonical CSS text; conditions cross as typed IR trees. + +mod alloc_shim; +mod canonical; +mod colors; +mod conditions; +mod expand; +mod ffi; +mod known_properties; + +use bombshell_css_core::selectors::validate_and_specificity; +use bombshell_css_core::{ + CompiledRule, CompiledStylesheet, ContainerQuery, Declaration, Diagnostic, IR_VERSION, Origin, +}; +use std::string::String; +use std::vec::Vec; + +/// Append `value` in decimal, without pulling `core::fmt`'s integer +/// formatting machinery into the module. +fn push_u32(out: &mut String, mut value: u32) { + let mut buf = [0u8; 10]; + let mut len = 0; + loop { + buf[len] = b'0' + (value % 10) as u8; + len += 1; + value /= 10; + if value == 0 { + break; + } + } + let bytes = &buf[..len]; + let reversed: Vec = bytes.iter().rev().copied().collect(); + // SAFETY: digits and `/`-free ASCII. + out.push_str(core::str::from_utf8(&reversed).unwrap_or("")); +} + +/// Options for [`compile`]. +#[derive(Debug, Clone)] +pub struct CompileOptions { + /// Keep parsing after errors, reporting diagnostics and preserving the + /// recoverable portion of the stylesheet. + pub error_recovery: bool, + /// Accepted for API compatibility; output is always canonical. + pub minify: bool, +} + +impl Default for CompileOptions { + fn default() -> Self { + CompileOptions { + error_recovery: true, + minify: true, + } + } +} + +/// The result of compiling a stylesheet. +#[derive(Debug, Clone, Default)] +pub struct CompileOutput { + /// The compiled IR, or `None` when compilation failed without recovery. + pub sheet: Option, + /// Errors and warnings collected during compilation. + pub diagnostics: Vec, +} + +/// Parse and compile CSS into the versioned stylesheet IR. +pub fn compile(css: &str, origin: Origin, options: CompileOptions) -> CompileOutput { + let Some(events) = ffi::parse_events(css) else { + return CompileOutput { + sheet: None, + diagnostics: vec![Diagnostic::error("parse", "stylesheet parse failed")], + }; + }; + + let mut walker = Walker { + options, + css, + warnings: Vec::new(), + out: CompiledStylesheet { + version: IR_VERSION, + origin, + rules: Vec::new(), + layer_order: Vec::new(), + keyframes: Vec::new(), + opaque: Vec::new(), + warnings: Vec::new(), + }, + stack: vec![Frame::Root], + next_order: 0, + anonymous_layers: 0, + last_at_name: None, + hard_error: None, + }; + + walker.run(&events); + walker.out.warnings = walker.warnings.clone(); + + if let Some(message) = walker.hard_error { + // Recovery warnings observed before the fatal error still matter to + // the host. + let mut diagnostics = Vec::with_capacity(walker.warnings.len() + 1); + diagnostics.extend(walker.warnings.clone()); + diagnostics.push(Diagnostic::error("parse", message)); + return CompileOutput { + sheet: None, + diagnostics, + }; + } + + // Warnings ride both the sheet (IR round-trip) and the compile output, + // so hosts that never decode the IR still see them. + CompileOutput { + sheet: Some(walker.out), + diagnostics: walker.warnings.clone(), + } +} + +/// Walk frames: the event stream is processed with an explicit stack. +enum Frame { + Root, + /// A style rule (or `@nest` rule) with its expanded selector context. + Style(StyleFrame), + /// A grouping at-rule whose context applies to nested rules. + At(AtFrame), + /// A subtree whose events are ignored (inside `@keyframes`/opaque + /// at-rules, or under a failed rule). + Skipper, +} + +#[derive(Clone)] +struct StyleFrame { + /// Expanded, validated selector text (comma-separated segments). + selector: String, + specificity: u32, + declarations: Vec, + /// The rule already emitted (before nested children walked). + emitted: bool, +} + +#[derive(Clone)] +enum AtFrame { + Media(bombshell_css_core::MediaCondition), + Container(ContainerQuery), + Supports(bombshell_css_core::SupportsCondition), + Layer(Vec), + Keyframes { name: String }, + Opaque, +} + +struct Walker<'t> { + options: CompileOptions, + css: &'t str, + warnings: Vec, + out: CompiledStylesheet, + stack: Vec, + next_order: u32, + anonymous_layers: u32, + last_at_name: Option, + hard_error: Option, +} + +impl<'t> Walker<'t> { + fn run(&mut self, events: &[ffi::LbxEvent]) { + for event in events { + self.event(event); + } + } + + fn event(&mut self, event: &ffi::LbxEvent) { + match event.kind { + ffi::AT_BEGIN => { + self.last_at_name = Some( + event + .span_a(self.css) + .trim() + .trim_start_matches('@') + .to_ascii_lowercase(), + ); + if self.in_keyframes() || self.in_opaque() { + self.stack.push(Frame::Skipper); + } + } + ffi::STYLE_BEGIN => self.style_begin(event), + ffi::STYLE_END => self.style_end(), + ffi::AT_PRELUDE => self.at_prelude(event), + ffi::AT_END => self.at_end(event), + ffi::DECL => self.declaration(event), + _ => {} + } + } + + fn fail(&mut self, message: impl Into) { + if self.hard_error.is_none() { + self.hard_error = Some(message.into()); + } + } + + fn warn(&mut self, code: &str, message: impl Into) { + self.warnings + .push(Diagnostic::warning(code, message.into())); + } + + /// The enclosing style frame's parent segments (for `&` expansion). + fn parent_segments(&self) -> Vec { + for frame in self.stack.iter().rev() { + if let Frame::Style(style) = frame { + return style + .selector + .split(',') + .map(|s| s.trim().to_string()) + .collect(); + } + } + Vec::new() + } + + fn in_keyframes(&self) -> bool { + self.stack + .iter() + .any(|frame| matches!(frame, Frame::At(AtFrame::Keyframes { .. }))) + } + + fn in_opaque(&self) -> bool { + self.stack + .iter() + .any(|frame| matches!(frame, Frame::At(AtFrame::Opaque))) + } + + fn style_begin(&mut self, event: &ffi::LbxEvent) { + self.flush_top_style(); + + if self.in_keyframes() || self.in_opaque() { + self.stack.push(Frame::Skipper); + return; + } + + let prelude = event.span_a(self.css); + let parents = self.parent_segments(); + + if event.flags & ffi::FLAG_FAILED != 0 { + if !self.options.error_recovery { + self.fail("invalid selector list"); + } else { + self.warn("selector", "a style rule has an invalid selector list"); + } + self.stack.push(Frame::Skipper); + return; + } + + if parents.is_empty() && prelude.contains('&') { + self.warn( + "nesting-orphan", + "a top-level selector contains `&` with no parent selector", + ); + } + + let expanded = expand::expand_selector_list(prelude, &parents); + + if let Some(frame) = self.build_style_frame(&expanded) { + self.stack.push(frame); + } else { + self.stack.push(Frame::Skipper); + } + } + + fn build_style_frame(&mut self, expanded: &str) -> Option { + let specificity = match validate_and_specificity(expanded) { + Ok(specificity) => specificity, + Err(()) => { + if !self.options.error_recovery { + self.fail({ + let mut message = String::from("invalid selector list `"); + message.push_str(expanded); + message.push('`'); + message + }); + } else { + self.warn( + "selector", + { + let mut message = String::from( + "a style rule has an invalid selector list `", + ); + message.push_str(expanded); + message.push('`'); + message + }, + ); + } + return None; + } + }; + + Some(Frame::Style(StyleFrame { + selector: expanded.to_string(), + specificity, + declarations: Vec::new(), + emitted: false, + })) + } + + fn style_end(&mut self) { + let Some(frame) = self.stack.pop() else { + return; + }; + + if let Frame::Style(style) = frame { + if !style.emitted { + self.emit_style(style); + } + } + } + + /// Emit the top style frame's rule before nested children walk, keeping + /// source order. Trailing declarations (after nested rules) are emitted + /// as their own rule against the parent selector, per the old + /// NestedDeclarations behavior. + fn flush_top_style(&mut self) { + let Some(Frame::Style(_)) = self.stack.last() else { + return; + }; + + let emitted; + if let Frame::Style(style) = self.stack.last_mut().unwrap() { + if style.declarations.is_empty() || style.emitted { + return; + } + style.emitted = true; + emitted = style.clone(); + } else { + return; + } + + self.emit_style(emitted); + } + + fn emit_style(&mut self, style: StyleFrame) { + if style.declarations.is_empty() { + return; + } + + let order = self.next_order; + self.next_order += 1; + + let (media, containers, supports, layer) = self.context(); + + self.out.rules.push(CompiledRule { + selector: style.selector, + specificity: style.specificity, + layer, + media, + containers, + supports, + declarations: style.declarations, + order, + }); + } + + /// Emit a rule carrying only the given declarations (used for trailing + /// declarations after nested rules; specificity 0 per the old + /// NestedDeclarations behavior). + fn emit_trailing(&mut self, selector: String, declarations: Vec) { + if declarations.is_empty() { + return; + } + + let order = self.next_order; + self.next_order += 1; + + let (media, containers, supports, layer) = self.context(); + + self.out.rules.push(CompiledRule { + selector, + specificity: 0, + layer, + media, + containers, + supports, + declarations, + order, + }); + } + + fn at_prelude(&mut self, event: &ffi::LbxEvent) { + if matches!(self.stack.last(), Some(Frame::Skipper)) { + return; + } + + self.flush_top_style(); + + let failed = event.flags & ffi::FLAG_FAILED != 0; + let raw = event.span_a(self.css).trim(); + // The shim may reconstruct the prelude span from the at-keyword; + // strip a leading `@name` when present. + let prelude = match raw.strip_prefix('@') { + Some(rest) => rest + .split_once(char::is_whitespace) + .map(|(_, rest)| rest.trim()) + .unwrap_or(""), + None => raw, + } + .to_string(); + let name = self.last_at_name.clone().unwrap_or_default(); + + match name.as_str() { + "media" => { + if std::env::var("LBX_DEBUG").is_ok() { + eprintln!("[at-prelude media] failed={failed} prelude={prelude:?}"); + } + let condition = if failed { + self.condition_failure("media"); + bombshell_css_core::MediaCondition::Unknown + } else { + let parsed = conditions::parse_media_prelude(&prelude); + if std::env::var("LBX_DEBUG").is_ok() { + eprintln!("[media-parsed] {parsed:#?}"); + } + parsed + }; + self.stack.push(Frame::At(AtFrame::Media(condition))); + } + "container" => { + if failed { + self.condition_failure("container"); + } + let (name, condition) = conditions::parse_container_prelude(&prelude); + self.stack + .push(Frame::At(AtFrame::Container(ContainerQuery { + name, + condition, + }))); + } + "supports" => { + let condition = if failed { + self.condition_failure("supports"); + bombshell_css_core::SupportsCondition::Unknown + } else { + conditions::parse_supports_prelude(&prelude) + }; + self.stack.push(Frame::At(AtFrame::Supports(condition))); + } + "layer" => { + let mut paths: Vec> = Vec::new(); + if prelude.is_empty() { + self.anonymous_layers += 1; + paths.push(vec![{ + let mut name = String::from("_anonymous-layer-"); + push_u32(&mut name, self.anonymous_layers); + name + }]); + } else { + for name in prelude.split(',') { + let path: Vec = + name.trim().split('.').map(str::to_string).collect(); + paths.push(path); + } + } + for path in &paths { + self.ensure_layer_path(path); + } + let path = paths.first().cloned().unwrap_or_default(); + self.stack.push(Frame::At(AtFrame::Layer(path))); + } + "keyframes" => { + self.stack.push(Frame::At(AtFrame::Keyframes { + name: unquote(&prelude), + })); + } + "nest" => { + let parents = self.parent_segments(); + if parents.is_empty() { + self.warn("nesting-orphan", "@nest rule has no enclosing style rule"); + self.stack.push(Frame::Skipper); + return; + } + let expanded = expand::expand_selector_list(&prelude, &parents); + if let Some(frame) = self.build_style_frame(&expanded) { + self.stack.push(frame); + } else { + self.stack.push(Frame::Skipper); + } + } + "import" => { + self.warn( + "unresolved-import", + "@import is not resolved by this pipeline; the rule is preserved verbatim", + ); + self.stack.push(Frame::At(AtFrame::Opaque)); + } + _ => { + self.stack.push(Frame::At(AtFrame::Opaque)); + } + } + } + + fn condition_failure(&mut self, kind: &str) { + if !self.options.error_recovery { + self.fail({ + let mut message = String::from("invalid "); + message.push_str(kind); + message.push_str(" prelude"); + message + }); + } else { + self.warn(kind, { + let mut message = String::from("an @"); + message.push_str(kind); + message.push_str(" prelude failed to parse"); + message + }); + } + } + + fn at_end(&mut self, event: &ffi::LbxEvent) { + let Some(frame) = self.stack.pop() else { + return; + }; + + match frame { + Frame::At(AtFrame::Opaque) => { + let text = event.span_a(self.css).trim().to_string(); + if !text.is_empty() { + self.out + .opaque + .push(bombshell_css_core::OpaqueRule { text }); + } + } + Frame::At(AtFrame::Keyframes { name }) => { + let body = event.span_a(self.css).trim().to_string(); + self.out + .keyframes + .push(bombshell_css_core::KeyframesRule { name, body }); + } + // Media, container, supports, and layer frames just pop; nested + // events already consumed the context. A `@nest` frame carries a + // style rule and emits like any other. + Frame::Style(style) => self.emit_style(style), + _ => {} + } + } + + fn declaration(&mut self, event: &ffi::LbxEvent) { + // Find the frame that owns this declaration. + let mut target: Option = None; + for (index, frame) in self.stack.iter().enumerate().rev() { + match frame { + Frame::Style(_) => { + target = Some(index); + break; + } + Frame::Skipper | Frame::At(AtFrame::Keyframes { .. } | AtFrame::Opaque) => return, + Frame::At(_) | Frame::Root => continue, + } + } + + let Some(index) = target else { + return; + }; + + let name = event.span_a(self.css).trim().to_string(); + let value = event.span_b(self.css).trim().to_string(); + + if event.flags & ffi::FLAG_FAILED != 0 { + if !self.options.error_recovery { + self.fail({ + let mut message = String::from("invalid declaration `"); + message.push_str(&name); + message.push('`'); + message + }); + } else { + self.warn("declaration", { + let mut message = String::from("invalid value for `"); + message.push_str(&name); + message.push('`'); + message + }); + } + return; + } + + if name.is_empty() { + return; + } + + let important = event.flags & ffi::FLAG_IMPORTANT != 0; + + let value = match canonical::canonicalize(&name, &value) { + Ok(value) => value, + Err(diagnostic) => { + if !self.options.error_recovery { + self.fail({ + let mut message = String::from("invalid declaration `"); + message.push_str(&name); + message.push_str(": "); + message.push_str(&value); + message.push('`'); + message + }); + } else { + self.warnings.push(diagnostic); + } + return; + } + }; + + let (already_emitted, selector) = match &self.stack[index] { + Frame::Style(style) => (style.emitted, style.selector.clone()), + _ => return, + }; + + if already_emitted { + let selector = selector + .split(',') + .map(|s| s.trim().to_string()) + .collect::>() + .join(","); + self.emit_trailing( + selector, + vec![Declaration { + name, + value, + important, + }], + ); + return; + } + + let Frame::Style(style) = &mut self.stack[index] else { + return; + }; + + // Canonicalized per rule: each property at most once, last value wins. + match style.declarations.iter_mut().find(|d| d.name == name) { + Some(existing) => { + existing.value = value; + existing.important = important; + } + None => style.declarations.push(Declaration { + name, + value, + important, + }), + } + } + + /// Cascade context carried by the at-rule frames enclosing the rule. + fn context( + &self, + ) -> ( + Vec, + Vec, + Vec, + Option>, + ) { + let mut media = Vec::new(); + let mut containers = Vec::new(); + let mut supports = Vec::new(); + let mut layer = None; + + for frame in &self.stack { + match frame { + Frame::At(AtFrame::Media(condition)) => media.push(condition.clone()), + Frame::At(AtFrame::Container(query)) => containers.push(query.clone()), + Frame::At(AtFrame::Supports(condition)) => supports.push(condition.clone()), + Frame::At(AtFrame::Layer(path)) => { + let mut joined: Vec = layer.clone().unwrap_or_default(); + joined.extend(path.clone()); + layer = Some(joined); + } + _ => {} + } + } + + (media, containers, supports, layer) + } + + fn ensure_layer_path(&mut self, path: &[String]) { + // Register the path and every ancestor prefix in first-appearance order. + for end in 1..=path.len() { + let prefix = &path[..end]; + if !self.out.layer_order.iter().any(|p| p == prefix) { + self.out.layer_order.push(prefix.to_vec()); + } + } + } +} + +fn unquote(text: &str) -> String { + let text = text.trim(); + if text.len() >= 2 + && (text.starts_with('"') && text.ends_with('"') + || text.starts_with('\'') && text.ends_with('\'')) + { + text[1..text.len() - 1].to_string() + } else { + text.to_string() + } +} diff --git a/experiments/css/crates/css-compiler/tests/compiler.rs b/experiments/css/crates/css-compiler/tests/compiler.rs new file mode 100644 index 0000000..818aff4 --- /dev/null +++ b/experiments/css/crates/css-compiler/tests/compiler.rs @@ -0,0 +1,125 @@ +use bombshell_css_compiler::compile; +use bombshell_css_core::{FeatureKind, FeatureValue, IR_VERSION, Origin, decode_ir, encode_ir}; + +fn compile_ok(css: &str) -> bombshell_css_core::CompiledStylesheet { + let out = compile(css, Origin::Author, Default::default()); + assert!( + out.diagnostics.is_empty(), + "unexpected diagnostics: {:?}", + out.diagnostics + ); + let sheet = out.sheet.expect("sheet should compile"); + assert_eq!(sheet.version, IR_VERSION); + sheet +} + +#[test] +fn compiles_nesting_expansion() { + let sheet = compile_ok( + r" + .card { + color: oklch(70% 0.2 40); + + & > .title { + font-weight: bold; + + &:hover { text-decoration: underline; } + } + } + ", + ); + let selectors: Vec<&str> = sheet.rules.iter().map(|r| r.selector.as_str()).collect(); + assert_eq!( + selectors, + vec![".card", ".card>.title", ".card>.title:hover"] + ); + assert_eq!(sheet.rules[0].declarations[0].name, "color"); + assert_eq!(sheet.rules[0].declarations[0].value, "oklch(70% .2 40)"); +} + +#[test] +fn compiles_conditions_and_layers() { + let sheet = compile_ok( + r" + @layer base; + @layer base.type { .a { color: red } } + @media (min-width: 24em) { + @container panel (inline-size > 45em) { + @supports (color: oklch(70% 0.2 40)) { + .b { color: blue } + } + } + } + ", + ); + assert_eq!(sheet.layer_order, vec![vec!["base"], vec!["base", "type"]]); + assert_eq!(sheet.rules.len(), 2); + assert_eq!( + sheet.rules[0].layer.as_deref(), + Some(["base".to_string(), "type".to_string()].as_slice()) + ); + let rule = &sheet.rules[1]; + assert_eq!(rule.layer, None); + assert_eq!(rule.media.len(), 1); + assert_eq!(rule.containers.len(), 1); + assert_eq!(rule.containers[0].name.as_deref(), Some("panel")); + assert_eq!(rule.supports.len(), 1); + + // min-width: 24em canonicalizes to width >= 384px (16px base em). + let bombshell_css_core::MediaCondition::Feature(feature) = &rule.media[0] else { + panic!("expected media feature"); + }; + assert_eq!(feature.name, "width"); + match &feature.kind { + FeatureKind::Range { + cmp: bombshell_css_core::Compare::GreaterThanEqual, + value: FeatureValue::RelativeLength { value: 24.0, unit }, + } => assert_eq!(unit, "em"), + other => panic!("unexpected feature kind: {other:?}"), + } +} + +#[test] +fn compiles_keyframes_and_preserves_opaque_rules() { + let sheet = compile_ok( + r" + @keyframes pulse { from { opacity: 1 } to { opacity: 0 } } + @font-face { font-family: X; src: url(x.woff2) } + @property --brand { syntax: ''; initial-value: red; inherits: true } + ", + ); + assert_eq!(sheet.keyframes.len(), 1); + assert_eq!(sheet.keyframes[0].name, "pulse"); + assert!(sheet.keyframes[0].body.starts_with("@keyframes pulse")); + assert_eq!(sheet.opaque.len(), 2); + assert!(sheet.opaque[0].text.starts_with("@font-face")); + assert!(sheet.opaque[1].text.starts_with("@property")); +} + +#[test] +fn reports_parse_errors() { + let out = compile( + ".a { color: this is not a color !! }", + Origin::Author, + bombshell_css_compiler::CompileOptions { + error_recovery: false, + ..Default::default() + }, + ); + assert!(out.sheet.is_none()); + assert!(!out.diagnostics.is_empty()); + assert_eq!( + out.diagnostics[0].severity, + bombshell_css_core::Severity::Error + ); +} + +#[test] +fn ir_round_trips_through_compiler() { + let sheet = compile_ok(".a { color: red; &:hover { color: blue } }"); + let bytes = encode_ir(&sheet); + let decoded = decode_ir(&bytes).unwrap(); + assert_eq!(decoded, sheet); + assert_eq!(decoded.rules.len(), 2); + assert_eq!(decoded.rules[1].selector, ".a:hover"); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/LICENSE b/experiments/css/crates/css-compiler/vendor/lexbor/LICENSE new file mode 100644 index 0000000..8b95d81 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/LICENSE @@ -0,0 +1,223 @@ + + 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 + + +Lexbor SUBCOMPONENTS: + +The Lexbor includes a number of subcomponents with +separate copyright notices and license terms. Your use of the source +code for the these subcomponents is subject to the terms and +conditions of the following licenses. Paths are relative to the lexbor +folder. + +Portions of source/lexbor/core/dtoa.h, source/lexbor/core/dtoa.c, +source/lexbor/core/strtod.h, source/lexbor/core/strtod.c, +source/lexbor/core/diyfp.h, source/lexbor/core/diyfp.c is based on +NGINX NJS project (https://github.com/nginx/njs/) which is +available under BSD 2-clause "Simplified" License. + +BSD-2-Clause license. + +Copyright (C) 2015-2019 NGINX, Inc. +Copyright (C) 2019-2025 F5, Inc. +Copyright (C) 2015-2021 Igor Sysoev +Copyright (C) 2017-2025 Dmitry Volyntsev +Copyright (C) 2019-2022 Alexander Borisov +Copyright (C) 2022-2025 Vadim Zhestikov +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. 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 AUTHOR 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 AUTHOR 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. diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.c new file mode 100644 index 0000000..ccf3005 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/array.h" + + +lexbor_array_t * +lexbor_array_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_array_t)); +} + +lxb_status_t +lexbor_array_init(lexbor_array_t *array, size_t size) +{ + if (array == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (size == 0) { + return LXB_STATUS_ERROR_TOO_SMALL_SIZE; + } + + array->length = 0; + array->size = size; + + array->list = lexbor_malloc(sizeof(void *) * size); + if (array->list == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_OK; +} + +void +lexbor_array_clean(lexbor_array_t *array) +{ + if (array != NULL) { + array->length = 0; + } +} + +lexbor_array_t * +lexbor_array_destroy(lexbor_array_t *array, bool self_destroy) +{ + if (array == NULL) + return NULL; + + if (array->list) { + array->length = 0; + array->size = 0; + array->list = lexbor_free(array->list); + } + + if (self_destroy) { + return lexbor_free(array); + } + + return array; +} + +void ** +lexbor_array_expand(lexbor_array_t *array, size_t up_to) +{ + void **list; + size_t new_size; + + if (array->length > (SIZE_MAX - up_to)) + return NULL; + + new_size = array->length + up_to; + list = lexbor_realloc(array->list, sizeof(void *) * new_size); + + if (list == NULL) + return NULL; + + array->list = list; + array->size = new_size; + + return list; +} + +lxb_status_t +lexbor_array_push(lexbor_array_t *array, void *value) +{ + if (array->length >= array->size) { + if ((lexbor_array_expand(array, 128) == NULL)) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + array->list[ array->length ] = value; + array->length++; + + return LXB_STATUS_OK; +} + +void * +lexbor_array_pop(lexbor_array_t *array) +{ + if (array->length == 0) { + return NULL; + } + + array->length--; + return array->list[ array->length ]; +} + +lxb_status_t +lexbor_array_insert(lexbor_array_t *array, size_t idx, void *value) +{ + if (idx >= array->length) { + size_t up_to = (idx - array->length) + 1; + + if (idx >= array->size) { + if ((lexbor_array_expand(array, up_to) == NULL)) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + memset(&array->list[array->length], 0, sizeof(void *) * up_to); + + array->list[ idx ] = value; + array->length += up_to; + + return LXB_STATUS_OK; + } + + if (array->length >= array->size) { + if ((lexbor_array_expand(array, 32) == NULL)) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + memmove(&array->list[idx + 1], &array->list[idx], + sizeof(void *) * (array->length - idx)); + + array->list[ idx ] = value; + array->length++; + + return LXB_STATUS_OK; +} + +lxb_status_t +lexbor_array_set(lexbor_array_t *array, size_t idx, void *value) +{ + if (idx >= array->length) { + size_t up_to = (idx - array->length) + 1; + + if (idx >= array->size) { + if ((lexbor_array_expand(array, up_to) == NULL)) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + memset(&array->list[array->length], 0, sizeof(void *) * up_to); + + array->length += up_to; + } + + array->list[idx] = value; + + return LXB_STATUS_OK; +} + +void +lexbor_array_delete(lexbor_array_t *array, size_t begin, size_t length) +{ + if (begin >= array->length || length == 0) { + return; + } + + size_t end_len = begin + length; + + if (end_len >= array->length) { + array->length = begin; + return; + } + + memmove(&array->list[begin], &array->list[end_len], + sizeof(void *) * (array->length - end_len)); + + array->length -= length; +} + +/* + * No inline functions. + */ +void * +lexbor_array_get_noi(lexbor_array_t *array, size_t idx) +{ + return lexbor_array_get(array, idx); +} + +size_t +lexbor_array_length_noi(lexbor_array_t *array) +{ + return lexbor_array_length(array); +} + +size_t +lexbor_array_size_noi(lexbor_array_t *array) +{ + return lexbor_array_size(array); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.h new file mode 100644 index 0000000..5dbdac7 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_ARRAY_H +#define LEXBOR_ARRAY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +typedef struct { + void **list; + size_t size; + size_t length; +} +lexbor_array_t; + + +LXB_API lexbor_array_t * +lexbor_array_create(void); + +LXB_API lxb_status_t +lexbor_array_init(lexbor_array_t *array, size_t size); + +LXB_API void +lexbor_array_clean(lexbor_array_t *array); + +LXB_API lexbor_array_t * +lexbor_array_destroy(lexbor_array_t *array, bool self_destroy); + + +LXB_API void ** +lexbor_array_expand(lexbor_array_t *array, size_t up_to); + + +LXB_API lxb_status_t +lexbor_array_push(lexbor_array_t *array, void *value); + +LXB_API void * +lexbor_array_pop(lexbor_array_t *array); + +LXB_API lxb_status_t +lexbor_array_insert(lexbor_array_t *array, size_t idx, void *value); + +LXB_API lxb_status_t +lexbor_array_set(lexbor_array_t *array, size_t idx, void *value); + +LXB_API void +lexbor_array_delete(lexbor_array_t *array, size_t begin, size_t length); + + +/* + * Inline functions + */ +lxb_inline void * +lexbor_array_get(lexbor_array_t *array, size_t idx) +{ + if (idx >= array->length) { + return NULL; + } + + return array->list[idx]; +} + +lxb_inline size_t +lexbor_array_length(lexbor_array_t *array) +{ + return array->length; +} + +lxb_inline size_t +lexbor_array_size(lexbor_array_t *array) +{ + return array->size; +} + +/* + * No inline functions for ABI. + */ +LXB_API void * +lexbor_array_get_noi(lexbor_array_t *array, size_t idx); + +LXB_API size_t +lexbor_array_length_noi(lexbor_array_t *array); + +LXB_API size_t +lexbor_array_size_noi(lexbor_array_t *array); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_ARRAY_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.c new file mode 100644 index 0000000..79a4c3a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.c @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/array_obj.h" + + +lexbor_array_obj_t * +lexbor_array_obj_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_array_obj_t)); +} + +lxb_status_t +lexbor_array_obj_init(lexbor_array_obj_t *array, + size_t size, size_t struct_size) +{ + if (array == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (size == 0 || struct_size == 0) { + return LXB_STATUS_ERROR_TOO_SMALL_SIZE; + } + + array->length = 0; + array->size = size; + array->struct_size = struct_size; + + array->list = lexbor_malloc(sizeof(uint8_t *) + * (array->size * struct_size)); + if (array->list == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_OK; +} + +void +lexbor_array_obj_clean(lexbor_array_obj_t *array) +{ + if (array != NULL) { + array->length = 0; + } +} + +lexbor_array_obj_t * +lexbor_array_obj_destroy(lexbor_array_obj_t *array, bool self_destroy) +{ + if (array == NULL) + return NULL; + + if (array->list) { + array->length = 0; + array->size = 0; + array->list = lexbor_free(array->list); + } + + if (self_destroy) { + return lexbor_free(array); + } + + return array; +} + +uint8_t * +lexbor_array_obj_expand(lexbor_array_obj_t *array, size_t up_to) +{ + uint8_t *list; + size_t new_size; + + if (array->length > (SIZE_MAX - up_to)) { + return NULL; + } + + new_size = array->length + up_to; + + list = lexbor_realloc(array->list, sizeof(uint8_t *) + * (new_size * array->struct_size)); + if (list == NULL) { + return NULL; + } + + array->list = list; + array->size = new_size; + + return list; +} + +void * +lexbor_array_obj_push(lexbor_array_obj_t *array) +{ + void *entry; + + if (array->length >= array->size) + { + if ((lexbor_array_obj_expand(array, 128) == NULL)) { + return NULL; + } + } + + entry = array->list + (array->length * array->struct_size); + array->length++; + + memset(entry, 0, array->struct_size); + + return entry; +} + +void * +lexbor_array_obj_push_wo_cls(lexbor_array_obj_t *array) +{ + void *entry; + + if (array->length >= array->size) { + if ((lexbor_array_obj_expand(array, 128) == NULL)) { + return NULL; + } + } + + entry = array->list + (array->length * array->struct_size); + array->length++; + + return entry; +} + +void * +lexbor_array_obj_push_n(lexbor_array_obj_t *array, size_t count) +{ + void *entry; + + if ((array->length + count) > array->size) { + if ((lexbor_array_obj_expand(array, count + 128) == NULL)) { + return NULL; + } + } + + entry = array->list + (array->length * array->struct_size); + array->length += count; + + return entry; +} + +void * +lexbor_array_obj_pop(lexbor_array_obj_t *array) +{ + if (array->length == 0) { + return NULL; + } + + array->length--; + return array->list + (array->length * array->struct_size); +} + +void +lexbor_array_obj_delete(lexbor_array_obj_t *array, size_t begin, size_t length) +{ + if (begin >= array->length || length == 0) { + return; + } + + size_t end_len = begin + length; + + if (end_len >= array->length) { + array->length = begin; + return; + } + + memmove(&array->list[ begin * array->struct_size ], + &array->list[ end_len * array->struct_size ], + sizeof(uint8_t *) + * ((array->length - end_len) * array->struct_size)); + + array->length -= length; +} + +/* + * No inline functions. + */ +void +lexbor_array_obj_erase_noi(lexbor_array_obj_t *array) +{ + lexbor_array_obj_erase(array); +} + +void * +lexbor_array_obj_get_noi(lexbor_array_obj_t *array, size_t idx) +{ + return lexbor_array_obj_get(array, idx); +} + +size_t +lexbor_array_obj_length_noi(lexbor_array_obj_t *array) +{ + return lexbor_array_obj_length(array); +} + +size_t +lexbor_array_obj_size_noi(lexbor_array_obj_t *array) +{ + return lexbor_array_obj_size(array); +} + +size_t +lexbor_array_obj_struct_size_noi(lexbor_array_obj_t *array) +{ + return lexbor_array_obj_struct_size(array); +} + +void * +lexbor_array_obj_last_noi(lexbor_array_obj_t *array) +{ + return lexbor_array_obj_last(array); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.h new file mode 100644 index 0000000..de6202b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/array_obj.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_ARRAY_OBJ_H +#define LEXBOR_ARRAY_OBJ_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +typedef struct { + uint8_t *list; + size_t size; + size_t length; + size_t struct_size; +} +lexbor_array_obj_t; + + +LXB_API lexbor_array_obj_t * +lexbor_array_obj_create(void); + +LXB_API lxb_status_t +lexbor_array_obj_init(lexbor_array_obj_t *array, + size_t size, size_t struct_size); + +LXB_API void +lexbor_array_obj_clean(lexbor_array_obj_t *array); + +LXB_API lexbor_array_obj_t * +lexbor_array_obj_destroy(lexbor_array_obj_t *array, bool self_destroy); + + +LXB_API uint8_t * +lexbor_array_obj_expand(lexbor_array_obj_t *array, size_t up_to); + + +LXB_API void * +lexbor_array_obj_push(lexbor_array_obj_t *array); + +LXB_API void * +lexbor_array_obj_push_wo_cls(lexbor_array_obj_t *array); + +LXB_API void * +lexbor_array_obj_push_n(lexbor_array_obj_t *array, size_t count); + +LXB_API void * +lexbor_array_obj_pop(lexbor_array_obj_t *array); + +LXB_API void +lexbor_array_obj_delete(lexbor_array_obj_t *array, size_t begin, size_t length); + + +/* + * Inline functions + */ +lxb_inline void +lexbor_array_obj_erase(lexbor_array_obj_t *array) +{ + memset(array, 0, sizeof(lexbor_array_obj_t)); +} + +lxb_inline void * +lexbor_array_obj_get(const lexbor_array_obj_t *array, size_t idx) +{ + if (idx >= array->length) { + return NULL; + } + + return array->list + (idx * array->struct_size); +} + +lxb_inline size_t +lexbor_array_obj_length(lexbor_array_obj_t *array) +{ + return array->length; +} + +lxb_inline size_t +lexbor_array_obj_size(lexbor_array_obj_t *array) +{ + return array->size; +} + +lxb_inline size_t +lexbor_array_obj_struct_size(lexbor_array_obj_t *array) +{ + return array->struct_size; +} + +lxb_inline void * +lexbor_array_obj_last(lexbor_array_obj_t *array) +{ + if (array->length == 0) { + return NULL; + } + + return array->list + ((array->length - 1) * array->struct_size); +} + + +/* + * No inline functions for ABI. + */ +LXB_API void +lexbor_array_obj_erase_noi(lexbor_array_obj_t *array); + +LXB_API void * +lexbor_array_obj_get_noi(lexbor_array_obj_t *array, size_t idx); + +LXB_API size_t +lexbor_array_obj_length_noi(lexbor_array_obj_t *array); + +LXB_API size_t +lexbor_array_obj_size_noi(lexbor_array_obj_t *array); + +LXB_API size_t +lexbor_array_obj_struct_size_noi(lexbor_array_obj_t *array); + +LXB_API void * +lexbor_array_obj_last_noi(lexbor_array_obj_t *array); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_ARRAY_OBJ_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.c new file mode 100644 index 0000000..a180d14 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.c @@ -0,0 +1,525 @@ +/* + * Copyright (C) 2018-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/avl.h" + + +lxb_inline short +lexbor_avl_node_height(lexbor_avl_node_t *node); + +lxb_inline short +lexbor_avl_node_balance_factor(lexbor_avl_node_t *node); + +lxb_inline void +lexbor_avl_node_set_height(lexbor_avl_node_t *node); + +static lexbor_avl_node_t * +lexbor_avl_node_rotate_right(lexbor_avl_node_t *pos); + +static lexbor_avl_node_t * +lexbor_avl_node_rotate_left(lexbor_avl_node_t *pos); + +static lexbor_avl_node_t * +lexbor_avl_node_balance(lexbor_avl_node_t *node, + lexbor_avl_node_t **scope); + +lxb_inline lexbor_avl_node_t * +lexbor_avl_find_min(lexbor_avl_node_t *node); + +lxb_inline void +lexbor_avl_rotate_for_delete(lexbor_avl_node_t *delete_node, + lexbor_avl_node_t *node, + lexbor_avl_node_t **root); + + +lexbor_avl_t * +lexbor_avl_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_avl_t)); +} + +lxb_status_t +lexbor_avl_init(lexbor_avl_t *avl, size_t chunk_len, size_t struct_size) +{ + if (avl == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (chunk_len == 0 + || (struct_size != 0 && struct_size < sizeof(lexbor_avl_node_t))) + { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + if (struct_size == 0) { + struct_size = sizeof(lexbor_avl_node_t); + } + + avl->last_right = NULL; + + avl->nodes = lexbor_dobject_create(); + return lexbor_dobject_init(avl->nodes, chunk_len, struct_size); +} + +void +lexbor_avl_clean(lexbor_avl_t *avl) +{ + avl->last_right = NULL; + + lexbor_dobject_clean(avl->nodes); +} + +lexbor_avl_t * +lexbor_avl_destroy(lexbor_avl_t *avl, bool self_destroy) +{ + if (avl == NULL) + return NULL; + + avl->nodes = lexbor_dobject_destroy(avl->nodes, true); + + if (self_destroy) { + return lexbor_free(avl); + } + + return avl; +} + +lexbor_avl_node_t * +lexbor_avl_node_make(lexbor_avl_t *avl, size_t type, void *value) +{ + lexbor_avl_node_t *node = lexbor_dobject_calloc(avl->nodes); + if (node == NULL) { + return NULL; + } + + node->type = type; + node->value = value; + + return node; +} + +void +lexbor_avl_node_clean(lexbor_avl_node_t *node) +{ + memset(node, 0, sizeof(lexbor_avl_node_t)); +} + +lexbor_avl_node_t * +lexbor_avl_node_destroy(lexbor_avl_t *avl, + lexbor_avl_node_t *node, bool self_destroy) +{ + if (node == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_dobject_free(avl->nodes, node); + } + + return node; +} + +lxb_inline short +lexbor_avl_node_height(lexbor_avl_node_t *node) +{ + return (node) ? node->height : 0; +} + +lxb_inline short +lexbor_avl_node_balance_factor(lexbor_avl_node_t *node) +{ + return (lexbor_avl_node_height(node->right) + - lexbor_avl_node_height(node->left)); +} + +lxb_inline void +lexbor_avl_node_set_height(lexbor_avl_node_t *node) +{ + short left_height = lexbor_avl_node_height(node->left); + short right_height = lexbor_avl_node_height(node->right); + + node->height = ((left_height > right_height) + ? left_height : right_height) + 1; +} + +static lexbor_avl_node_t * +lexbor_avl_node_rotate_right(lexbor_avl_node_t *pos) +{ + lexbor_avl_node_t *node = pos->left; + + node->parent = pos->parent; + + if (node->right) { + node->right->parent = pos; + } + + pos->left = node->right; + pos->parent = node; + + node->right = pos; + + lexbor_avl_node_set_height(pos); + lexbor_avl_node_set_height(node); + + return node; +} + +static lexbor_avl_node_t * +lexbor_avl_node_rotate_left(lexbor_avl_node_t *pos) +{ + lexbor_avl_node_t *node = pos->right; + + node->parent = pos->parent; + + if (node->left) { + node->left->parent = pos; + } + + pos->right = node->left; + pos->parent = node; + + node->left = pos; + + lexbor_avl_node_set_height(pos); + lexbor_avl_node_set_height(node); + + return node; +} + +static lexbor_avl_node_t * +lexbor_avl_node_balance(lexbor_avl_node_t *node, lexbor_avl_node_t **scope) +{ + /* Set height */ + lexbor_avl_node_t *parent; + + short left_height = lexbor_avl_node_height(node->left); + short right_height = lexbor_avl_node_height(node->right); + + node->height = ((left_height > right_height) + ? left_height : right_height) + 1; + + /* Check balance */ + switch ((right_height - left_height)) { + case 2: { + if (lexbor_avl_node_balance_factor(node->right) < 0) { + node->right = lexbor_avl_node_rotate_right(node->right); + } + + parent = node->parent; + + if (parent != NULL) { + if (parent->right == node) { + parent->right = lexbor_avl_node_rotate_left(node); + return parent->right; + } + else { + parent->left = lexbor_avl_node_rotate_left(node); + return parent->left; + } + } + + return lexbor_avl_node_rotate_left(node); + } + case -2: { + if (lexbor_avl_node_balance_factor(node->left) > 0) { + node->left = lexbor_avl_node_rotate_left(node->left); + } + + parent = node->parent; + + if (parent != NULL) { + if (parent->right == node) { + parent->right = lexbor_avl_node_rotate_right(node); + return parent->right; + } + else { + parent->left = lexbor_avl_node_rotate_right(node); + return parent->left; + } + } + + return lexbor_avl_node_rotate_right(node); + } + default: + break; + } + + if (node->parent == NULL) { + *scope = node; + } + + return node->parent; +} + +lexbor_avl_node_t * +lexbor_avl_insert(lexbor_avl_t *avl, lexbor_avl_node_t **scope, + size_t type, void *value) +{ + lexbor_avl_node_t *node, *new_node; + + if (*scope == NULL) { + *scope = lexbor_avl_node_make(avl, type, value); + return *scope; + } + + node = *scope; + new_node = lexbor_dobject_calloc(avl->nodes); + + for (;;) { + if (type == node->type) { + node->value = value; + return node; + } + else if (type < node->type) { + if (node->left == NULL) { + node->left = new_node; + + new_node->parent = node; + new_node->type = type; + new_node->value = value; + + node = new_node; + break; + } + + node = node->left; + } + else { + if (node->right == NULL) { + node->right = new_node; + + new_node->parent = node; + new_node->type = type; + new_node->value = value; + + node = new_node; + break; + } + + node = node->right; + } + } + + while (node != NULL) { + node = lexbor_avl_node_balance(node, scope); + } + + return new_node; +} + +lxb_inline lexbor_avl_node_t * +lexbor_avl_find_min(lexbor_avl_node_t *node) +{ + if (node == NULL) { + return NULL; + } + + while (node->right != NULL) { + node = node->right; + } + + return node; +} + +lxb_inline void +lexbor_avl_rotate_for_delete(lexbor_avl_node_t *delete_node, + lexbor_avl_node_t *node, lexbor_avl_node_t **scope) +{ + lexbor_avl_node_t *balance_node; + + if (node) { + if (delete_node->left == node) { + balance_node = (node->left) ? node->left : node; + + node->parent = delete_node->parent; + node->right = delete_node->right; + + if (delete_node->right) + delete_node->right->parent = node; + } + else { + balance_node = node; + + node->parent->right = NULL; + + node->parent = delete_node->parent; + node->right = delete_node->right; + node->left = delete_node->left; + + if (delete_node->left != NULL) { + delete_node->left->parent = node; + } + + if (delete_node->right != NULL) { + delete_node->right->parent = node; + } + } + + if (delete_node->parent != NULL) { + if (delete_node->parent->left == delete_node) { + delete_node->parent->left = node; + } + else { + delete_node->parent->right = node; + } + } + else { + *scope = node; + } + } + else { + balance_node = delete_node->parent; + + if (balance_node != NULL) { + if (balance_node->left == delete_node) { + balance_node->left = delete_node->right; + } + else { + balance_node->right = delete_node->right; + } + } + else { + *scope = delete_node->right; + } + + if (delete_node->right != NULL) { + delete_node->right->parent = balance_node; + } + } + + while (balance_node != NULL) { + balance_node = lexbor_avl_node_balance(balance_node, scope); + } +} + +void * +lexbor_avl_remove(lexbor_avl_t *avl, lexbor_avl_node_t **scope, size_t type) +{ + void *value; + lexbor_avl_node_t *node = *scope; + + while (node != NULL) { + if (type == node->type) { + avl->last_right = lexbor_avl_find_min(node->left); + lexbor_avl_rotate_for_delete(node, avl->last_right, scope); + + value = node->value; + lexbor_dobject_free(avl->nodes, node); + + return value; + } + else if (type < node->type) { + node = node->left; + } + else { + node = node->right; + } + } + + return NULL; +} + +void +lexbor_avl_remove_by_node(lexbor_avl_t *avl, lexbor_avl_node_t **root, + lexbor_avl_node_t *node) +{ + avl->last_right = lexbor_avl_find_min(node->left); + + lexbor_avl_rotate_for_delete(node, avl->last_right, root); + + (void) lexbor_dobject_free(avl->nodes, node); +} + +lexbor_avl_node_t * +lexbor_avl_search(lexbor_avl_t *avl, lexbor_avl_node_t *node, size_t type) +{ + while (node != NULL) { + if (type == node->type) { + return node; + } + else if (type < node->type) { + node = node->left; + } + else { + node = node->right; + } + } + + return NULL; +} + +lxb_inline lexbor_avl_node_t * +lexbor_avl_search_ge(lexbor_avl_node_t *root, size_t type) +{ + lexbor_avl_node_t *result = NULL; + + while (root != NULL) { + if (type == root->type) { + return root; + } + else if (type < root->type) { + result = root; + root = root->left; + } + else { + root = root->right; + } + } + + return result; +} + +lxb_status_t +lexbor_avl_foreach(lexbor_avl_t *avl, lexbor_avl_node_t **scope, + lexbor_avl_node_f cb, void *ctx) +{ + size_t next_type; + lxb_status_t status; + lexbor_avl_node_t *node; + + if (scope == NULL || *scope == NULL) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + /* Find the leftmost (smallest) node. */ + + node = *scope; + + while (node->left != NULL) { + node = node->left; + } + + while (node != NULL) { + next_type = node->type + 1; + + status = cb(avl, scope, node, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (*scope == NULL) { + return LXB_STATUS_OK; + } + + node = lexbor_avl_search_ge(*scope, next_type); + } + + return LXB_STATUS_OK; +} + +void +lexbor_avl_foreach_recursion(lexbor_avl_t *avl, lexbor_avl_node_t *scope, + lexbor_avl_node_f callback, void *ctx) +{ + if (scope == NULL) { + return; + } + + callback(avl, NULL, scope, ctx); + + lexbor_avl_foreach_recursion(avl, scope->left, callback, ctx); + lexbor_avl_foreach_recursion(avl, scope->right, callback, ctx); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.h new file mode 100644 index 0000000..c1a61f6 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/avl.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_AVL_H +#define LEXBOR_AVL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" +#include "lexbor/core/dobject.h" + + +typedef struct lexbor_avl lexbor_avl_t; +typedef struct lexbor_avl_node lexbor_avl_node_t; + +typedef lxb_status_t +(*lexbor_avl_node_f)(lexbor_avl_t *avl, lexbor_avl_node_t **root, + lexbor_avl_node_t *node, void *ctx); + +struct lexbor_avl_node { + size_t type; + short height; + void *value; + + lexbor_avl_node_t *left; + lexbor_avl_node_t *right; + lexbor_avl_node_t *parent; +}; + +struct lexbor_avl { + lexbor_dobject_t *nodes; + lexbor_avl_node_t *last_right; +}; + + +LXB_API lexbor_avl_t * +lexbor_avl_create(void); + +LXB_API lxb_status_t +lexbor_avl_init(lexbor_avl_t *avl, size_t chunk_len, size_t struct_size); + +LXB_API void +lexbor_avl_clean(lexbor_avl_t *avl); + +LXB_API lexbor_avl_t * +lexbor_avl_destroy(lexbor_avl_t *avl, bool self_destroy); + + +LXB_API lexbor_avl_node_t * +lexbor_avl_node_make(lexbor_avl_t *avl, size_t type, void *value); + +LXB_API void +lexbor_avl_node_clean(lexbor_avl_node_t *node); + +LXB_API lexbor_avl_node_t * +lexbor_avl_node_destroy(lexbor_avl_t *avl, lexbor_avl_node_t *node, + bool self_destroy); + + +LXB_API lexbor_avl_node_t * +lexbor_avl_insert(lexbor_avl_t *avl, lexbor_avl_node_t **scope, + size_t type, void *value); + +LXB_API lexbor_avl_node_t * +lexbor_avl_search(lexbor_avl_t *avl, lexbor_avl_node_t *scope, size_t type); + +LXB_API void * +lexbor_avl_remove(lexbor_avl_t *avl, lexbor_avl_node_t **scope, size_t type); + +LXB_API void +lexbor_avl_remove_by_node(lexbor_avl_t *avl, lexbor_avl_node_t **root, + lexbor_avl_node_t *node); + +LXB_API lxb_status_t +lexbor_avl_foreach(lexbor_avl_t *avl, lexbor_avl_node_t **scope, + lexbor_avl_node_f cb, void *ctx); + +LXB_API void +lexbor_avl_foreach_recursion(lexbor_avl_t *avl, lexbor_avl_node_t *scope, + lexbor_avl_node_f callback, void *ctx); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_AVL_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/base.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/base.h new file mode 100644 index 0000000..4b30ab0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/base.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_BASE_H +#define LEXBOR_BASE_H + +#ifdef __cplusplus +#define __STDC_LIMIT_MACROS +#define __STDC_CONSTANT_MACROS + +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "lexbor/core/def.h" +#include "lexbor/core/types.h" +#include "lexbor/core/lexbor.h" + +#define LEXBOR_VERSION_MAJOR 3 +#define LEXBOR_VERSION_MINOR 1 +#define LEXBOR_VERSION_PATCH 0 + +#define LEXBOR_VERSION_STRING LEXBOR_STRINGIZE(LEXBOR_VERSION_MAJOR) "." \ + LEXBOR_STRINGIZE(LEXBOR_VERSION_MINOR) "." \ + LEXBOR_STRINGIZE(LEXBOR_VERSION_PATCH) + +#define lexbor_assert(val) + +#define lexbor_max(val1, val2) ((val1) > (val2) ? (val1) : (val2)) +#define lexbor_min(val1, val2) ((val1) < (val2) ? (val1) : (val2)) + +#define LEXBOR_STR_RES_MAP_CHAR_OTHER 0x00 +#define LEXBOR_STR_RES_MAP_CHAR_A_Z_a_z 0x01 +#define LEXBOR_STR_RES_MAP_CHAR_WHITESPACE 0x02 +#define LEXBOR_STR_RES_SLIP 0xFF + +/* + * Very important!!! + * + * for lexbor 0..00AFFF; LXB_STATUS_OK == 0x000000 + */ +typedef enum { + LXB_STATUS_OK = 0x0000, + LXB_STATUS_ERROR = 0x0001, + LXB_STATUS_ERROR_MEMORY_ALLOCATION, + LXB_STATUS_ERROR_OBJECT_IS_NULL, + LXB_STATUS_ERROR_SMALL_BUFFER, + LXB_STATUS_ERROR_INCOMPLETE_OBJECT, + LXB_STATUS_ERROR_NO_FREE_SLOT, + LXB_STATUS_ERROR_TOO_SMALL_SIZE, + LXB_STATUS_ERROR_NOT_EXISTS, + LXB_STATUS_ERROR_WRONG_ARGS, + LXB_STATUS_ERROR_WRONG_STAGE, + LXB_STATUS_ERROR_UNEXPECTED_RESULT, + LXB_STATUS_ERROR_UNEXPECTED_DATA, + LXB_STATUS_ERROR_OVERFLOW, + LXB_STATUS_CONTINUE, + LXB_STATUS_SMALL_BUFFER, + LXB_STATUS_ABORTED, + LXB_STATUS_STOPPED, + LXB_STATUS_NEXT, + LXB_STATUS_STOP, + LXB_STATUS_WARNING, + LXB_STATUS_SKIPPED +} +lexbor_status_t; + +typedef enum { + LEXBOR_ACTION_OK = 0x00, + LEXBOR_ACTION_STOP = 0x01, + LEXBOR_ACTION_NEXT = 0x02 +} +lexbor_action_t; + + +typedef lxb_status_t +(*lexbor_serialize_cb_f)(const lxb_char_t *data, size_t len, void *ctx); + +typedef lxb_status_t +(*lexbor_serialize_cb_cp_f)(const lxb_codepoint_t *cps, size_t len, void *ctx); + + +typedef struct { + lexbor_serialize_cb_f cb; + void *ctx; + + intptr_t opt; + size_t count; +} +lexbor_serialize_ctx_t; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_BASE_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.c new file mode 100644 index 0000000..60e79cf --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.c @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/bst.h" +#include "lexbor/core/conv.h" + + +lexbor_bst_t * +lexbor_bst_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_bst_t)); +} + +lxb_status_t +lexbor_bst_init(lexbor_bst_t *bst, size_t size) +{ + lxb_status_t status; + + if (bst == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + bst->dobject = lexbor_dobject_create(); + status = lexbor_dobject_init(bst->dobject, size, + sizeof(lexbor_bst_entry_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + bst->root = 0; + bst->tree_length = 0; + + return LXB_STATUS_OK; +} + +void +lexbor_bst_clean(lexbor_bst_t *bst) +{ + if (bst != NULL) { + lexbor_dobject_clean(bst->dobject); + + bst->root = 0; + bst->tree_length = 0; + } +} + +lexbor_bst_t * +lexbor_bst_destroy(lexbor_bst_t *bst, bool self_destroy) +{ + if (bst == NULL) { + return NULL; + } + + bst->dobject = lexbor_dobject_destroy(bst->dobject, true); + + if (self_destroy) { + return lexbor_free(bst); + } + + return bst; +} + +lexbor_bst_entry_t * +lexbor_bst_entry_make(lexbor_bst_t *bst, size_t size) +{ + lexbor_bst_entry_t *new_entry = lexbor_dobject_calloc(bst->dobject); + if (new_entry == NULL) { + return NULL; + } + + new_entry->size = size; + + bst->tree_length++; + + return new_entry; +} + +lexbor_bst_entry_t * +lexbor_bst_insert(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, + size_t size, void *value) +{ + lexbor_bst_entry_t *new_entry, *entry; + + new_entry = lexbor_dobject_calloc(bst->dobject); + if (new_entry == NULL) { + return NULL; + } + + new_entry->size = size; + new_entry->value = value; + + bst->tree_length++; + + if (*scope == NULL) { + *scope = new_entry; + return new_entry; + } + + entry = *scope; + + while (entry != NULL) { + if (size == entry->size) { + if (entry->next) { + new_entry->next = entry->next; + } + + entry->next = new_entry; + new_entry->parent = entry->parent; + + return new_entry; + } + else if (size > entry->size) { + if (entry->right == NULL) { + entry->right = new_entry; + new_entry->parent = entry; + + return new_entry; + } + + entry = entry->right; + } + else { + if (entry->left == NULL) { + entry->left = new_entry; + new_entry->parent = entry; + + return new_entry; + } + + entry = entry->left; + } + } + + return NULL; +} + +lexbor_bst_entry_t * +lexbor_bst_insert_not_exists(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, + size_t size) +{ + lexbor_bst_entry_t *entry; + + if (*scope == NULL) { + *scope = lexbor_bst_entry_make(bst, size); + + return *scope; + } + + entry = *scope; + + while (entry != NULL) { + if (size == entry->size) { + return entry; + } + else if (size > entry->size) { + if (entry->right == NULL) { + entry->right = lexbor_bst_entry_make(bst, size); + entry->right->parent = entry; + + return entry->right; + } + + entry = entry->right; + } + else { + if (entry->left == NULL) { + entry->left = lexbor_bst_entry_make(bst, size); + entry->left->parent = entry; + + return entry->left; + } + + entry = entry->left; + } + } + + return NULL; +} + +lexbor_bst_entry_t * +lexbor_bst_search(lexbor_bst_t *bst, lexbor_bst_entry_t *scope, size_t size) +{ + while (scope != NULL) { + if (scope->size == size) { + return scope; + } + else if (size > scope->size) { + scope = scope->right; + } + else { + scope = scope->left; + } + } + + return NULL; +} + +lexbor_bst_entry_t * +lexbor_bst_search_close(lexbor_bst_t *bst, lexbor_bst_entry_t *scope, + size_t size) +{ + lexbor_bst_entry_t *max = NULL; + + while (scope != NULL) { + if (scope->size == size) { + return scope; + } + else if (size > scope->size) { + scope = scope->right; + } + else { + max = scope; + scope = scope->left; + } + } + + return max; +} + +void * +lexbor_bst_remove(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, size_t size) +{ + lexbor_bst_entry_t *entry = *scope; + + while (entry != NULL) { + if (entry->size == size) { + return lexbor_bst_remove_by_pointer(bst, entry, scope); + } + else if (size > entry->size) { + entry = entry->right; + } + else { + entry = entry->left; + } + } + + return NULL; +} + +void * +lexbor_bst_remove_close(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, + size_t size, size_t *found_size) +{ + lexbor_bst_entry_t *entry = *scope; + lexbor_bst_entry_t *max = NULL; + + while (entry != NULL) { + if (entry->size == size) { + if (found_size) { + *found_size = entry->size; + } + + return lexbor_bst_remove_by_pointer(bst, entry, scope); + } + else if (size > entry->size) { + entry = entry->right; + } + else { + max = entry; + entry = entry->left; + } + } + + if (max != NULL) { + if (found_size != NULL) { + *found_size = max->size; + } + + return lexbor_bst_remove_by_pointer(bst, max, scope); + } + + if (found_size != NULL) { + *found_size = 0; + } + + return NULL; +} + +void * +lexbor_bst_remove_by_pointer(lexbor_bst_t *bst, lexbor_bst_entry_t *entry, + lexbor_bst_entry_t **root) +{ + void *value; + lexbor_bst_entry_t *next, *right, *left; + + bst->tree_length--; + + if (entry->next != NULL) { + next = entry->next; + entry->next = entry->next->next; + + value = next->value; + + lexbor_dobject_free(bst->dobject, next); + + return value; + } + + value = entry->value; + + if (entry->left == NULL && entry->right == NULL) { + if (entry->parent != NULL) { + if (entry->parent->left == entry) entry->parent->left = NULL; + if (entry->parent->right == entry) entry->parent->right = NULL; + } + else { + *root = NULL; + } + + lexbor_dobject_free(bst->dobject, entry); + } + else if (entry->left == NULL) { + if (entry->parent == NULL) { + entry->right->parent = NULL; + + *root = entry->right; + + lexbor_dobject_free(bst->dobject, entry); + + entry = *root; + } + else { + right = entry->right; + right->parent = entry->parent; + + memcpy(entry, right, sizeof(lexbor_bst_entry_t)); + + lexbor_dobject_free(bst->dobject, right); + } + + if (entry->right != NULL) { + entry->right->parent = entry; + } + + if (entry->left != NULL) { + entry->left->parent = entry; + } + } + else if (entry->right == NULL) { + if (entry->parent == NULL) { + entry->left->parent = NULL; + + *root = entry->left; + + lexbor_dobject_free(bst->dobject, entry); + + entry = *root; + } + else { + left = entry->left; + left->parent = entry->parent; + + memcpy(entry, left, sizeof(lexbor_bst_entry_t)); + + lexbor_dobject_free(bst->dobject, left); + } + + if (entry->right != NULL) { + entry->right->parent = entry; + } + + if (entry->left != NULL) { + entry->left->parent = entry; + } + } + else { + left = entry->right; + + while (left->left != NULL) { + left = left->left; + } + + /* Swap */ + entry->size = left->size; + entry->next = left->next; + entry->value = left->value; + + /* Change parrent */ + if (entry->right == left) { + entry->right = left->right; + + if (entry->right != NULL) { + left->right->parent = entry; + } + } + else { + left->parent->left = left->right; + + if (left->right != NULL) { + left->right->parent = left->parent; + } + } + + lexbor_dobject_free(bst->dobject, left); + } + + return value; +} + +void +lexbor_bst_serialize(lexbor_bst_t *bst, lexbor_callback_f callback, void *ctx) +{ + lexbor_bst_serialize_entry(bst->root, callback, ctx, 0); +} + +void +lexbor_bst_serialize_entry(lexbor_bst_entry_t *entry, + lexbor_callback_f callback, void *ctx, size_t tabs) +{ + size_t len; + lxb_char_t buff[1024]; + + if (entry == NULL) { + return; + } + + /* Left */ + for (size_t i = 0; i < tabs; i++) { + callback((lxb_char_t *) "\t", 1, ctx); + } + callback((lxb_char_t *) "left) { + len = lexbor_conv_int64_to_data((int64_t) entry->left->size, + buff, sizeof(buff)); + callback(buff, len, ctx); + + callback((lxb_char_t *) ">\n", 2, ctx); + lexbor_bst_serialize_entry(entry->left, callback, ctx, (tabs + 1)); + + for (size_t i = 0; i < tabs; i++) { + callback((lxb_char_t *) "\t", 1, ctx); + } + } + else { + callback((lxb_char_t *) "NULL>", 5, ctx); + } + + callback((lxb_char_t *) "\n", 8, ctx); + + /* Right */ + for (size_t i = 0; i < tabs; i++) { + callback((lxb_char_t *) "\t", 1, ctx); + } + callback((lxb_char_t *) "right) { + len = lexbor_conv_int64_to_data((int64_t) entry->right->size, + buff, sizeof(buff)); + callback(buff, len, ctx); + + callback((lxb_char_t *) ">\n", 2, ctx); + lexbor_bst_serialize_entry(entry->right, callback, ctx, (tabs + 1)); + + for (size_t i = 0; i < tabs; i++) { + callback((lxb_char_t *) "\t", 1, ctx); + } + } + else { + callback((lxb_char_t *) "NULL>", 5, ctx); + } + + callback((lxb_char_t *) "\n", 9, ctx); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.h new file mode 100644 index 0000000..3d7bf88 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst.h @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_BST_H +#define LEXBOR_BST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" +#include "lexbor/core/dobject.h" + + +#define lexbor_bst_root(bst) (bst)->root +#define lexbor_bst_root_ref(bst) &((bst)->root) + + +typedef struct lexbor_bst_entry lexbor_bst_entry_t; +typedef struct lexbor_bst lexbor_bst_t; + +typedef bool (*lexbor_bst_entry_f)(lexbor_bst_t *bst, + lexbor_bst_entry_t *entry, void *ctx); + +struct lexbor_bst_entry { + void *value; + + lexbor_bst_entry_t *right; + lexbor_bst_entry_t *left; + lexbor_bst_entry_t *next; + lexbor_bst_entry_t *parent; + + size_t size; +}; + +struct lexbor_bst { + lexbor_dobject_t *dobject; + lexbor_bst_entry_t *root; + + size_t tree_length; +}; + + +LXB_API lexbor_bst_t * +lexbor_bst_create(void); + +LXB_API lxb_status_t +lexbor_bst_init(lexbor_bst_t *bst, size_t size); + +LXB_API void +lexbor_bst_clean(lexbor_bst_t *bst); + +LXB_API lexbor_bst_t * +lexbor_bst_destroy(lexbor_bst_t *bst, bool self_destroy); + +LXB_API lexbor_bst_entry_t * +lexbor_bst_entry_make(lexbor_bst_t *bst, size_t size); + +LXB_API lexbor_bst_entry_t * +lexbor_bst_insert(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, + size_t size, void *value); + +LXB_API lexbor_bst_entry_t * +lexbor_bst_insert_not_exists(lexbor_bst_t *bst, lexbor_bst_entry_t **scope, + size_t size); + + +LXB_API lexbor_bst_entry_t * +lexbor_bst_search(lexbor_bst_t *bst, lexbor_bst_entry_t *scope, size_t size); + +LXB_API lexbor_bst_entry_t * +lexbor_bst_search_close(lexbor_bst_t *bst, lexbor_bst_entry_t *scope, + size_t size); + + +LXB_API void * +lexbor_bst_remove(lexbor_bst_t *bst, lexbor_bst_entry_t **root, size_t size); + +LXB_API void * +lexbor_bst_remove_close(lexbor_bst_t *bst, lexbor_bst_entry_t **root, + size_t size, size_t *found_size); + +LXB_API void * +lexbor_bst_remove_by_pointer(lexbor_bst_t *bst, lexbor_bst_entry_t *entry, + lexbor_bst_entry_t **root); + + +LXB_API void +lexbor_bst_serialize(lexbor_bst_t *bst, lexbor_callback_f callback, void *ctx); + +LXB_API void +lexbor_bst_serialize_entry(lexbor_bst_entry_t *entry, + lexbor_callback_f callback, void *ctx, size_t tabs); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_BST_H */ + + + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.c new file mode 100644 index 0000000..bc1c8bd --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/bst_map.h" +#include "lexbor/core/utils.h" + + +lexbor_bst_map_t * +lexbor_bst_map_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_bst_map_t)); +} + +lxb_status_t +lexbor_bst_map_init(lexbor_bst_map_t *bst_map, size_t size) +{ + lxb_status_t status; + + if (bst_map == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + /* bst */ + bst_map->bst = lexbor_bst_create(); + status = lexbor_bst_init(bst_map->bst, size); + if (status) { + return status; + } + + /* dobject */ + bst_map->entries = lexbor_dobject_create(); + status = lexbor_dobject_init(bst_map->entries, size, + sizeof(lexbor_bst_map_entry_t)); + if (status) { + return status; + } + + /* mraw */ + bst_map->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(bst_map->mraw, (size * 6)); + if (status) { + return status; + } + + return LXB_STATUS_OK; +} + +void +lexbor_bst_map_clean(lexbor_bst_map_t *bst_map) +{ + lexbor_bst_clean(bst_map->bst); + lexbor_mraw_clean(bst_map->mraw); + lexbor_dobject_clean(bst_map->entries); +} + +lexbor_bst_map_t * +lexbor_bst_map_destroy(lexbor_bst_map_t *bst_map, bool self_destroy) +{ + if (bst_map == NULL) { + return NULL; + } + + bst_map->bst = lexbor_bst_destroy(bst_map->bst, true); + bst_map->mraw = lexbor_mraw_destroy(bst_map->mraw, true); + bst_map->entries = lexbor_dobject_destroy(bst_map->entries, true); + + if (self_destroy) { + return lexbor_free(bst_map); + } + + return bst_map; +} + +lexbor_bst_map_entry_t * +lexbor_bst_map_search(lexbor_bst_map_t *bst_map, lexbor_bst_entry_t *scope, + const lxb_char_t *key, size_t key_len) +{ + lexbor_bst_map_entry_t *entry; + lexbor_bst_entry_t *bst_entry; + + size_t hash_id = lexbor_utils_hash_hash(key, key_len); + + bst_entry = lexbor_bst_search(bst_map->bst, scope, hash_id); + if (bst_entry == NULL) { + return NULL; + } + + do { + entry = bst_entry->value; + + if (entry->str.length == key_len && + lexbor_str_data_cmp(entry->str.data, key)) + { + return entry; + } + + bst_entry = bst_entry->next; + } + while (bst_entry != NULL); + + return NULL; +} + +lexbor_bst_map_entry_t * +lexbor_bst_map_insert(lexbor_bst_map_t *bst_map, + lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len, void *value) +{ + lexbor_bst_map_entry_t *entry; + + entry = lexbor_bst_map_insert_not_exists(bst_map, scope, key, key_len); + if (entry == NULL) { + return NULL; + } + + entry->value = value; + + return entry; +} + +lexbor_bst_map_entry_t * +lexbor_bst_map_insert_not_exists(lexbor_bst_map_t *bst_map, + lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len) +{ + lexbor_bst_map_entry_t *entry; + lexbor_bst_entry_t *bst_entry; + + size_t hash_id = lexbor_utils_hash_hash(key, key_len); + + bst_entry = lexbor_bst_insert_not_exists(bst_map->bst, scope, hash_id); + if (bst_entry == NULL) { + return NULL; + } + + if (bst_entry->value == NULL) { + goto new_entry; + } + + do { + entry = bst_entry->value; + + if (entry->str.length == key_len && + lexbor_str_data_cmp(entry->str.data, key)) + { + return entry; + } + + if (bst_entry->next == NULL) { + bst_entry->next = lexbor_bst_entry_make(bst_map->bst, hash_id); + bst_entry = bst_entry->next; + + if (bst_entry == NULL) { + return NULL; + } + + goto new_entry; + } + + bst_entry = bst_entry->next; + } + while (1); + + return NULL; + +new_entry: + + entry = lexbor_dobject_calloc(bst_map->entries); + if (entry == NULL) { + return NULL; + } + + lexbor_str_init(&entry->str, bst_map->mraw, key_len); + if (entry->str.data == NULL) { + lexbor_dobject_free(bst_map->entries, entry); + + return NULL; + } + + lexbor_str_append(&entry->str, bst_map->mraw, key, key_len); + + bst_entry->value = entry; + + return entry; +} + +void * +lexbor_bst_map_remove(lexbor_bst_map_t *bst_map, lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len) +{ + lexbor_bst_map_entry_t *entry; + lexbor_bst_entry_t *bst_entry; + + size_t hash_id = lexbor_utils_hash_hash(key, key_len); + + bst_entry = lexbor_bst_search(bst_map->bst, *scope, hash_id); + if (bst_entry == NULL) { + return NULL; + } + + do { + entry = bst_entry->value; + + if (entry->str.length == key_len && + lexbor_str_data_cmp(entry->str.data, key)) + { + void *value = entry->value; + + lexbor_bst_remove_by_pointer(bst_map->bst, bst_entry, scope); + + lexbor_str_destroy(&entry->str, bst_map->mraw, false); + lexbor_dobject_free(bst_map->entries, entry); + + return value; + } + + bst_entry = bst_entry->next; + } + while (bst_entry != NULL); + + return NULL; +} + +/* + * No inline functions. + */ +lexbor_mraw_t * +lexbor_bst_map_mraw_noi(lexbor_bst_map_t *bst_map) +{ + return lexbor_bst_map_mraw(bst_map); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.h new file mode 100644 index 0000000..69b9faa --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/bst_map.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_BST_MAP_H +#define LEXBOR_BST_MAP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/bst.h" +#include "lexbor/core/str.h" +#include "lexbor/core/mraw.h" +#include "lexbor/core/dobject.h" + + +typedef struct { + lexbor_str_t str; + void *value; +} +lexbor_bst_map_entry_t; + +typedef struct { + lexbor_bst_t *bst; + lexbor_mraw_t *mraw; + lexbor_dobject_t *entries; + +} +lexbor_bst_map_t; + + +LXB_API lexbor_bst_map_t * +lexbor_bst_map_create(void); + +LXB_API lxb_status_t +lexbor_bst_map_init(lexbor_bst_map_t *bst_map, size_t size); + +LXB_API void +lexbor_bst_map_clean(lexbor_bst_map_t *bst_map); + +LXB_API lexbor_bst_map_t * +lexbor_bst_map_destroy(lexbor_bst_map_t *bst_map, bool self_destroy); + + +LXB_API lexbor_bst_map_entry_t * +lexbor_bst_map_search(lexbor_bst_map_t *bst_map, lexbor_bst_entry_t *scope, + const lxb_char_t *key, size_t key_len); + +LXB_API lexbor_bst_map_entry_t * +lexbor_bst_map_insert(lexbor_bst_map_t *bst_map, lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len, void *value); + +LXB_API lexbor_bst_map_entry_t * +lexbor_bst_map_insert_not_exists(lexbor_bst_map_t *bst_map, + lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len); + +LXB_API void * +lexbor_bst_map_remove(lexbor_bst_map_t *bst_map, lexbor_bst_entry_t **scope, + const lxb_char_t *key, size_t key_len); + + +/* + * Inline functions + */ +lxb_inline lexbor_mraw_t * +lexbor_bst_map_mraw(lexbor_bst_map_t *bst_map) +{ + return bst_map->mraw; +} + +/* + * No inline functions for ABI. + */ +LXB_API lexbor_mraw_t * +lexbor_bst_map_mraw_noi(lexbor_bst_map_t *bst_map); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_BST_MAP_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.c new file mode 100644 index 0000000..b694c5a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.c @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include +#include + +#include "lexbor/core/conv.h" +#include "lexbor/core/dtoa.h" +#include "lexbor/core/strtod.h" + + +size_t +lexbor_conv_float_to_data(double num, lxb_char_t *buf, size_t len) +{ + return lexbor_dtoa(num, buf, len); +} + +size_t +lexbor_conv_long_to_data(long num, lxb_char_t *buf, size_t len) +{ + return lexbor_conv_int64_to_data((int64_t) num, buf, len); +} + +size_t +lexbor_conv_int64_to_data(int64_t num, lxb_char_t *buf, size_t len) +{ + int64_t tmp; + size_t have_minus, i, length; + + static const lxb_char_t *digits = (const lxb_char_t *) "0123456789"; + + if (num != 0) { + tmp = num; + length = 0; + have_minus = 0; + + if (num < 0) { + length = 1; + num = -num; + have_minus = 1; + } + + while (tmp != 0) { + length += 1; + tmp /= 10; + } + + /* length += (size_t) floor(log10(labs((long) num))) + 1; */ + } + else { + if (len > 0) { + buf[0] = '0'; + return 1; + } + + return 0; + } + + if (len < length) { + i = (length + have_minus) - len; + + while (i != have_minus) { + i -= 1; + num /= 10; + } + + length = len; + } + + if (have_minus) { + buf[0] = '-'; + } + + i = length; + if (length < len) { + buf[length] = '\0'; + } + + while (i != have_minus) { + i -= 1; + buf[i] = digits[ num % 10 ]; + num /= 10; + } + + return length; +} + +double +lexbor_conv_data_to_double(const lxb_char_t **start, size_t len) +{ + int exponent, exp, insignf; + lxb_char_t c, *pos; + bool minus, ex_minus; + double num; + const lxb_char_t *e, *p, *last, *end; + lxb_char_t data[128]; + + end = *start + len; + + exponent = 0; + insignf = 0; + + pos = data; + last = data + sizeof(data); + + minus = false; + + switch (**start) { + case '-': + minus = true; + /* fall through */ + case '+': + (*start)++; + /* fall through */ + default: + break; + } + + for (p = *start; p < end; p++) { + /* Values less than '0' become >= 208. */ + c = *p - '0'; + + if (c > 9) { + break; + } + + if (pos < last) { + *pos++ = *p; + } + else { + insignf++; + } + } + + /* Do not emit a '.', but adjust the exponent instead. */ + if (p < end && *p == '.') { + + for (p++; p < end; p++) { + /* Values less than '0' become >= 208. */ + c = *p - '0'; + + if (c > 9) { + break; + } + + if (pos < last) { + *pos++ = *p; + exponent--; + } + else { + /* Ignore insignificant digits in the fractional part. */ + } + } + } + + e = p + 1; + + if (e < end && (*p == 'e' || *p == 'E')) { + ex_minus = 0; + + if (e + 1 < end) { + if (*e == '-') { + e++; + ex_minus = 1; + } + else if (*e == '+') { + e++; + } + } + + /* Values less than '0' become >= 208. */ + c = *e - '0'; + + if (c <= 9) { + exp = c; + + for (p = e + 1; p < end; p++) { + /* Values less than '0' become >= 208. */ + c = *p - '0'; + + if (c > 9) { + break; + } + + exp = exp * 10 + c; + } + + exponent += ex_minus ? -exp : exp; + } + } + + *start = p; + + exponent += insignf; + + num = lexbor_strtod_internal(data, pos - data, exponent); + + if (minus) { + num = -num; + } + + return num; +} + +unsigned long +lexbor_conv_data_to_ulong(const lxb_char_t **data, size_t length) +{ + const lxb_char_t *p = *data; + const lxb_char_t *end = p + length; + unsigned long last_number = 0, number = 0; + + for (; p < end; p++) { + if (*p < '0' || *p > '9') { + goto done; + } + + number = (*p - '0') + number * 10; + + if (last_number > number) { + *data = p - 1; + return last_number; + } + + last_number = number; + } + +done: + + *data = p; + + return number; +} + +long +lexbor_conv_data_to_long(const lxb_char_t **data, size_t length) +{ + bool minus; + const lxb_char_t *p; + const lxb_char_t *end; + unsigned long n = 0, number = 0; + + minus = false; + p = *data; + end = p + length; + + switch (*p) { + case '-': + minus = true; + /* fall through */ + case '+': + p++; + /* fall through */ + default: + break; + } + + for (; p < end; p++) { + if (*p < '0' || *p > '9') { + break; + } + + n = (*p - '0') + number * 10; + + if (n > LONG_MAX) { + p -= 1; + break; + } + + number = n; + } + + *data = p; + + return (minus) ? -number : number; +} + +unsigned +lexbor_conv_data_to_uint(const lxb_char_t **data, size_t length) +{ + const lxb_char_t *p = *data; + const lxb_char_t *end = p + length; + unsigned last_number = 0, number = 0; + + for (; p < end; p++) { + if (*p < '0' || *p > '9') { + goto done; + } + + number = (*p - '0') + number * 10; + + if (last_number > number) { + *data = p - 1; + return last_number; + } + + last_number = number; + } + +done: + + *data = p; + + return number; +} + +size_t +lexbor_conv_dec_to_hex(uint32_t number, lxb_char_t *out, size_t length, + bool upper) +{ + lxb_char_t c; + size_t len; + uint32_t tmp; + const lxb_char_t *map_str; + + static const lxb_char_t map_str_l[] = "0123456789abcdef"; + static const lxb_char_t map_str_u[] = "0123456789ABCDEF"; + + map_str = (upper) ? map_str_u : map_str_l; + + if (number != 0) { + tmp = number; + len = 0; + + while (tmp != 0) { + len += 1; + tmp /= 16; + } + } + else { + if (length > 0) { + out[0] = '0'; + return 1; + } + + return 0; + } + + while (len > length) { + number /= 16; + len -= 1; + } + + tmp = (uint32_t) len; + + while (tmp != 0) { + tmp -= 1; + c = number % 16; + number = number / 16; + out[tmp] = map_str[c]; + } + + return len; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.h new file mode 100644 index 0000000..df2bfca --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/conv.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CONV_H +#define LEXBOR_CONV_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/core/base.h" + + +LXB_API size_t +lexbor_conv_float_to_data(double num, lxb_char_t *buf, size_t len); + +LXB_API size_t +lexbor_conv_long_to_data(long num, lxb_char_t *buf, size_t len); + +LXB_API size_t +lexbor_conv_int64_to_data(int64_t num, lxb_char_t *buf, size_t len); + +LXB_API double +lexbor_conv_data_to_double(const lxb_char_t **start, size_t len); + +LXB_API unsigned long +lexbor_conv_data_to_ulong(const lxb_char_t **data, size_t length); + +LXB_API long +lexbor_conv_data_to_long(const lxb_char_t **data, size_t length); + +LXB_API unsigned +lexbor_conv_data_to_uint(const lxb_char_t **data, size_t length); + +LXB_API size_t +lexbor_conv_dec_to_hex(uint32_t number, lxb_char_t *out, size_t length, + bool upper); + +lxb_inline long +lexbor_conv_double_to_long(double number) +{ + if (number > (double) LONG_MAX) { + return LONG_MAX; + } + + if (number < (double) LONG_MIN) { + return -LONG_MAX; + } + + return (long) number; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CONV_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/core.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/core.h new file mode 100644 index 0000000..047254e --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/core.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CORE_H +#define LEXBOR_CORE_H + +#include "lexbor/core/base.h" +#include "lexbor/core/def.h" +#include "lexbor/core/types.h" +#include "lexbor/core/array.h" +#include "lexbor/core/array_obj.h" +#include "lexbor/core/avl.h" +#include "lexbor/core/bst.h" +#include "lexbor/core/bst_map.h" +#include "lexbor/core/dobject.h" +#include "lexbor/core/fs.h" +#include "lexbor/core/in.h" +#include "lexbor/core/lexbor.h" +#include "lexbor/core/mem.h" +#include "lexbor/core/mraw.h" +#include "lexbor/core/perf.h" +#include "lexbor/core/sbst.h" +#include "lexbor/core/shs.h" +#include "lexbor/core/str.h" +#include "lexbor/core/conv.h" +#include "lexbor/core/utils.h" +#include "lexbor/core/diyfp.h" +#include "lexbor/core/dtoa.h" +#include "lexbor/core/strtod.h" +#include "lexbor/core/serialize.h" + +#endif /* LEXBOR_CORE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/def.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/def.h new file mode 100644 index 0000000..c8b9440 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/def.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_DEF_H +#define LEXBOR_DEF_H + +#define LEXBOR_STRINGIZE_HELPER(x) #x +#define LEXBOR_STRINGIZE(x) LEXBOR_STRINGIZE_HELPER(x) + +/* Format */ +#ifdef _WIN32 + #define LEXBOR_FORMAT_Z "%Iu" +#else + #define LEXBOR_FORMAT_Z "%zu" +#endif + +/* Deprecated */ +#ifdef _MSC_VER + #define LXB_DEPRECATED(func) __declspec(deprecated) func +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) + #define LXB_DEPRECATED(func) func __attribute__((deprecated)) +#else + #define LXB_DEPRECATED(func) func +#endif + +/* Debug */ +//#define LEXBOR_DEBUG(...) do {} while (0) +//#define LEXBOR_DEBUG_ERROR(...) do {} while (0) + +#define LEXBOR_MEM_ALIGN_STEP sizeof(void *) + +#ifndef LEXBOR_STATIC + #ifdef _WIN32 + #ifdef LEXBOR_BUILDING + #define LXB_API __declspec(dllexport) + #else + #define LXB_API __declspec(dllimport) + #endif + #elif (defined(__SUNPRO_C) || defined(__SUNPRO_CC)) + #define LXB_API __global + #else + #if (defined(__GNUC__) && __GNUC__ >= 4) || defined(__INTEL_COMPILER) + #define LXB_API __attribute__ ((visibility("default"))) + #else + #define LXB_API + #endif + #endif +#else + #define LXB_API +#endif + +#define LXB_EXTERN extern LXB_API + +#define lxb_size(_size) ((size_t) (_size)) + +#endif /* LEXBOR_DEF_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.c new file mode 100644 index 0000000..7bdbcbb --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + * + * Based on nxt_diyfp.h from NGINX NJS project + * An internal diy_fp implementation. + * For details, see Loitsch, Florian. "Printing floating-point numbers quickly + * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243. + */ + + +#include "lexbor/core/diyfp.h" + + +typedef struct { + uint64_t significand; + int16_t bin_exp; + int16_t dec_exp; +} +lexbor_diyfp_cpe_t; + + +static const lexbor_diyfp_cpe_t lexbor_cached_powers[] = { + { lexbor_uint64_hl(0xfa8fd5a0, 0x081c0288), -1220, -348 }, + { lexbor_uint64_hl(0xbaaee17f, 0xa23ebf76), -1193, -340 }, + { lexbor_uint64_hl(0x8b16fb20, 0x3055ac76), -1166, -332 }, + { lexbor_uint64_hl(0xcf42894a, 0x5dce35ea), -1140, -324 }, + { lexbor_uint64_hl(0x9a6bb0aa, 0x55653b2d), -1113, -316 }, + { lexbor_uint64_hl(0xe61acf03, 0x3d1a45df), -1087, -308 }, + { lexbor_uint64_hl(0xab70fe17, 0xc79ac6ca), -1060, -300 }, + { lexbor_uint64_hl(0xff77b1fc, 0xbebcdc4f), -1034, -292 }, + { lexbor_uint64_hl(0xbe5691ef, 0x416bd60c), -1007, -284 }, + { lexbor_uint64_hl(0x8dd01fad, 0x907ffc3c), -980, -276 }, + { lexbor_uint64_hl(0xd3515c28, 0x31559a83), -954, -268 }, + { lexbor_uint64_hl(0x9d71ac8f, 0xada6c9b5), -927, -260 }, + { lexbor_uint64_hl(0xea9c2277, 0x23ee8bcb), -901, -252 }, + { lexbor_uint64_hl(0xaecc4991, 0x4078536d), -874, -244 }, + { lexbor_uint64_hl(0x823c1279, 0x5db6ce57), -847, -236 }, + { lexbor_uint64_hl(0xc2109436, 0x4dfb5637), -821, -228 }, + { lexbor_uint64_hl(0x9096ea6f, 0x3848984f), -794, -220 }, + { lexbor_uint64_hl(0xd77485cb, 0x25823ac7), -768, -212 }, + { lexbor_uint64_hl(0xa086cfcd, 0x97bf97f4), -741, -204 }, + { lexbor_uint64_hl(0xef340a98, 0x172aace5), -715, -196 }, + { lexbor_uint64_hl(0xb23867fb, 0x2a35b28e), -688, -188 }, + { lexbor_uint64_hl(0x84c8d4df, 0xd2c63f3b), -661, -180 }, + { lexbor_uint64_hl(0xc5dd4427, 0x1ad3cdba), -635, -172 }, + { lexbor_uint64_hl(0x936b9fce, 0xbb25c996), -608, -164 }, + { lexbor_uint64_hl(0xdbac6c24, 0x7d62a584), -582, -156 }, + { lexbor_uint64_hl(0xa3ab6658, 0x0d5fdaf6), -555, -148 }, + { lexbor_uint64_hl(0xf3e2f893, 0xdec3f126), -529, -140 }, + { lexbor_uint64_hl(0xb5b5ada8, 0xaaff80b8), -502, -132 }, + { lexbor_uint64_hl(0x87625f05, 0x6c7c4a8b), -475, -124 }, + { lexbor_uint64_hl(0xc9bcff60, 0x34c13053), -449, -116 }, + { lexbor_uint64_hl(0x964e858c, 0x91ba2655), -422, -108 }, + { lexbor_uint64_hl(0xdff97724, 0x70297ebd), -396, -100 }, + { lexbor_uint64_hl(0xa6dfbd9f, 0xb8e5b88f), -369, -92 }, + { lexbor_uint64_hl(0xf8a95fcf, 0x88747d94), -343, -84 }, + { lexbor_uint64_hl(0xb9447093, 0x8fa89bcf), -316, -76 }, + { lexbor_uint64_hl(0x8a08f0f8, 0xbf0f156b), -289, -68 }, + { lexbor_uint64_hl(0xcdb02555, 0x653131b6), -263, -60 }, + { lexbor_uint64_hl(0x993fe2c6, 0xd07b7fac), -236, -52 }, + { lexbor_uint64_hl(0xe45c10c4, 0x2a2b3b06), -210, -44 }, + { lexbor_uint64_hl(0xaa242499, 0x697392d3), -183, -36 }, + { lexbor_uint64_hl(0xfd87b5f2, 0x8300ca0e), -157, -28 }, + { lexbor_uint64_hl(0xbce50864, 0x92111aeb), -130, -20 }, + { lexbor_uint64_hl(0x8cbccc09, 0x6f5088cc), -103, -12 }, + { lexbor_uint64_hl(0xd1b71758, 0xe219652c), -77, -4 }, + { lexbor_uint64_hl(0x9c400000, 0x00000000), -50, 4 }, + { lexbor_uint64_hl(0xe8d4a510, 0x00000000), -24, 12 }, + { lexbor_uint64_hl(0xad78ebc5, 0xac620000), 3, 20 }, + { lexbor_uint64_hl(0x813f3978, 0xf8940984), 30, 28 }, + { lexbor_uint64_hl(0xc097ce7b, 0xc90715b3), 56, 36 }, + { lexbor_uint64_hl(0x8f7e32ce, 0x7bea5c70), 83, 44 }, + { lexbor_uint64_hl(0xd5d238a4, 0xabe98068), 109, 52 }, + { lexbor_uint64_hl(0x9f4f2726, 0x179a2245), 136, 60 }, + { lexbor_uint64_hl(0xed63a231, 0xd4c4fb27), 162, 68 }, + { lexbor_uint64_hl(0xb0de6538, 0x8cc8ada8), 189, 76 }, + { lexbor_uint64_hl(0x83c7088e, 0x1aab65db), 216, 84 }, + { lexbor_uint64_hl(0xc45d1df9, 0x42711d9a), 242, 92 }, + { lexbor_uint64_hl(0x924d692c, 0xa61be758), 269, 100 }, + { lexbor_uint64_hl(0xda01ee64, 0x1a708dea), 295, 108 }, + { lexbor_uint64_hl(0xa26da399, 0x9aef774a), 322, 116 }, + { lexbor_uint64_hl(0xf209787b, 0xb47d6b85), 348, 124 }, + { lexbor_uint64_hl(0xb454e4a1, 0x79dd1877), 375, 132 }, + { lexbor_uint64_hl(0x865b8692, 0x5b9bc5c2), 402, 140 }, + { lexbor_uint64_hl(0xc83553c5, 0xc8965d3d), 428, 148 }, + { lexbor_uint64_hl(0x952ab45c, 0xfa97a0b3), 455, 156 }, + { lexbor_uint64_hl(0xde469fbd, 0x99a05fe3), 481, 164 }, + { lexbor_uint64_hl(0xa59bc234, 0xdb398c25), 508, 172 }, + { lexbor_uint64_hl(0xf6c69a72, 0xa3989f5c), 534, 180 }, + { lexbor_uint64_hl(0xb7dcbf53, 0x54e9bece), 561, 188 }, + { lexbor_uint64_hl(0x88fcf317, 0xf22241e2), 588, 196 }, + { lexbor_uint64_hl(0xcc20ce9b, 0xd35c78a5), 614, 204 }, + { lexbor_uint64_hl(0x98165af3, 0x7b2153df), 641, 212 }, + { lexbor_uint64_hl(0xe2a0b5dc, 0x971f303a), 667, 220 }, + { lexbor_uint64_hl(0xa8d9d153, 0x5ce3b396), 694, 228 }, + { lexbor_uint64_hl(0xfb9b7cd9, 0xa4a7443c), 720, 236 }, + { lexbor_uint64_hl(0xbb764c4c, 0xa7a44410), 747, 244 }, + { lexbor_uint64_hl(0x8bab8eef, 0xb6409c1a), 774, 252 }, + { lexbor_uint64_hl(0xd01fef10, 0xa657842c), 800, 260 }, + { lexbor_uint64_hl(0x9b10a4e5, 0xe9913129), 827, 268 }, + { lexbor_uint64_hl(0xe7109bfb, 0xa19c0c9d), 853, 276 }, + { lexbor_uint64_hl(0xac2820d9, 0x623bf429), 880, 284 }, + { lexbor_uint64_hl(0x80444b5e, 0x7aa7cf85), 907, 292 }, + { lexbor_uint64_hl(0xbf21e440, 0x03acdd2d), 933, 300 }, + { lexbor_uint64_hl(0x8e679c2f, 0x5e44ff8f), 960, 308 }, + { lexbor_uint64_hl(0xd433179d, 0x9c8cb841), 986, 316 }, + { lexbor_uint64_hl(0x9e19db92, 0xb4e31ba9), 1013, 324 }, + { lexbor_uint64_hl(0xeb96bf6e, 0xbadf77d9), 1039, 332 }, + { lexbor_uint64_hl(0xaf87023b, 0x9bf0ee6b), 1066, 340 }, +}; + + +#define LEXBOR_DIYFP_D_1_LOG2_10 0.30102999566398114 /* 1 / log2(10). */ + + +lexbor_diyfp_t +lexbor_cached_power_dec(int exp, int *dec_exp) +{ + unsigned int index; + const lexbor_diyfp_cpe_t *cp; + + index = (exp + LEXBOR_DECIMAL_EXPONENT_OFF) / LEXBOR_DECIMAL_EXPONENT_DIST; + cp = &lexbor_cached_powers[index]; + + *dec_exp = cp->dec_exp; + + return lexbor_diyfp(cp->significand, cp->bin_exp); +} + +lexbor_diyfp_t +lexbor_cached_power_bin(int exp, int *dec_exp) +{ + int k; + unsigned int index; + const lexbor_diyfp_cpe_t *cp; + + k = (int) ceil((-61 - exp) * LEXBOR_DIYFP_D_1_LOG2_10) + + LEXBOR_DECIMAL_EXPONENT_OFF - 1; + + index = (unsigned) (k >> 3) + 1; + + cp = &lexbor_cached_powers[index]; + + *dec_exp = -(LEXBOR_DECIMAL_EXPONENT_MIN + (int) (index << 3)); + + return lexbor_diyfp(cp->significand, cp->bin_exp); +} + + +#undef LEXBOR_DIYFP_D_1_LOG2_10 diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.h new file mode 100644 index 0000000..86c5336 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/diyfp.h @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + * + * Based on nxt_diyfp.h from NGINX NJS project + * An internal diy_fp implementation. + * For details, see Loitsch, Florian. "Printing floating-point numbers quickly + * and accurately with integers." ACM Sigplan Notices 45.6 (2010): 233-243. + */ + +#ifndef LEXBOR_DIYFP_H +#define LEXBOR_DIYFP_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + +#include + + +#ifdef __cplusplus +#define lexbor_diyfp(_s, _e) { .significand = (_s), .exp = (int) (_e) } +#else +#define lexbor_diyfp(_s, _e) (lexbor_diyfp_t) \ + { .significand = (_s), .exp = (int) (_e) } +#endif +#define lexbor_uint64_hl(h, l) (((uint64_t) (h) << 32) + (l)) + + +#define LEXBOR_DBL_SIGNIFICAND_SIZE 52 +#define LEXBOR_DBL_EXPONENT_BIAS (0x3FF + LEXBOR_DBL_SIGNIFICAND_SIZE) +#define LEXBOR_DBL_EXPONENT_MIN (-LEXBOR_DBL_EXPONENT_BIAS) +#define LEXBOR_DBL_EXPONENT_MAX (0x7FF - LEXBOR_DBL_EXPONENT_BIAS) +#define LEXBOR_DBL_EXPONENT_DENORMAL (-LEXBOR_DBL_EXPONENT_BIAS + 1) + +#define LEXBOR_DBL_SIGNIFICAND_MASK lexbor_uint64_hl(0x000FFFFF, 0xFFFFFFFF) +#define LEXBOR_DBL_HIDDEN_BIT lexbor_uint64_hl(0x00100000, 0x00000000) +#define LEXBOR_DBL_EXPONENT_MASK lexbor_uint64_hl(0x7FF00000, 0x00000000) + +#define LEXBOR_DIYFP_SIGNIFICAND_SIZE 64 + +#define LEXBOR_SIGNIFICAND_SIZE 53 +#define LEXBOR_SIGNIFICAND_SHIFT (LEXBOR_DIYFP_SIGNIFICAND_SIZE \ + - LEXBOR_DBL_SIGNIFICAND_SIZE) + +#define LEXBOR_DECIMAL_EXPONENT_OFF 348 +#define LEXBOR_DECIMAL_EXPONENT_MIN (-348) +#define LEXBOR_DECIMAL_EXPONENT_MAX 340 +#define LEXBOR_DECIMAL_EXPONENT_DIST 8 + + +typedef struct { + uint64_t significand; + int exp; +} +lexbor_diyfp_t; + + +LXB_API lexbor_diyfp_t +lexbor_cached_power_dec(int exp, int *dec_exp); + +LXB_API lexbor_diyfp_t +lexbor_cached_power_bin(int exp, int *dec_exp); + + +/* + * Inline functions + */ +#ifdef LEXBOR_HAVE_BUILTIN_CLZLL +#define nxt_leading_zeros64(x) (((x) == 0) ? 64 : __builtin_clzll(x)) + +#else + +lxb_inline uint64_t +lexbor_diyfp_leading_zeros64(uint64_t x) +{ + uint64_t n; + + if (x == 0) { + return 64; + } + + n = 0; + + while ((x & 0x8000000000000000) == 0) { + n++; + x <<= 1; + } + + return n; +} + +#endif + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_from_d2(double d) +{ + int biased_exp; + uint64_t significand; + lexbor_diyfp_t r; + + union { + double d; + uint64_t u64; + } + u; + + u.d = d; + + biased_exp = (u.u64 & LEXBOR_DBL_EXPONENT_MASK) + >> LEXBOR_DBL_SIGNIFICAND_SIZE; + significand = u.u64 & LEXBOR_DBL_SIGNIFICAND_MASK; + + if (biased_exp != 0) { + r.significand = significand + LEXBOR_DBL_HIDDEN_BIT; + r.exp = biased_exp - LEXBOR_DBL_EXPONENT_BIAS; + } + else { + r.significand = significand; + r.exp = LEXBOR_DBL_EXPONENT_MIN + 1; + } + + return r; +} + +lxb_inline double +lexbor_diyfp_2d(lexbor_diyfp_t v) +{ + int exp; + uint64_t significand, biased_exp; + + union { + double d; + uint64_t u64; + } + u; + + exp = v.exp; + significand = v.significand; + + while (significand > LEXBOR_DBL_HIDDEN_BIT + LEXBOR_DBL_SIGNIFICAND_MASK) { + significand >>= 1; + exp++; + } + + if (exp >= LEXBOR_DBL_EXPONENT_MAX) { + return INFINITY; + } + + if (exp < LEXBOR_DBL_EXPONENT_DENORMAL) { + return 0.0; + } + + while (exp > LEXBOR_DBL_EXPONENT_DENORMAL + && (significand & LEXBOR_DBL_HIDDEN_BIT) == 0) + { + significand <<= 1; + exp--; + } + + if (exp == LEXBOR_DBL_EXPONENT_DENORMAL + && (significand & LEXBOR_DBL_HIDDEN_BIT) == 0) + { + biased_exp = 0; + + } else { + biased_exp = (uint64_t) (exp + LEXBOR_DBL_EXPONENT_BIAS); + } + + u.u64 = (significand & LEXBOR_DBL_SIGNIFICAND_MASK) + | (biased_exp << LEXBOR_DBL_SIGNIFICAND_SIZE); + + return u.d; +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_shift_left(lexbor_diyfp_t v, unsigned shift) +{ + return lexbor_diyfp(v.significand << shift, v.exp - shift); +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_shift_right(lexbor_diyfp_t v, unsigned shift) +{ + return lexbor_diyfp(v.significand >> shift, v.exp + shift); +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_sub(lexbor_diyfp_t lhs, lexbor_diyfp_t rhs) +{ + return lexbor_diyfp(lhs.significand - rhs.significand, lhs.exp); +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_mul(lexbor_diyfp_t lhs, lexbor_diyfp_t rhs) +{ +#ifdef LEXBOR_HAVE_UNSIGNED_INT128 + + uint64_t l, h; + lxb_uint128_t u128; + + u128 = (lxb_uint128_t) (lhs.significand) + * (lxb_uint128_t) (rhs.significand); + + h = u128 >> 64; + l = (uint64_t) u128; + + /* rounding. */ + + if (l & ((uint64_t) 1 << 63)) { + h++; + } + + return lexbor_diyfp(h, lhs.exp + rhs.exp + 64); + +#else + + uint64_t a, b, c, d, ac, bc, ad, bd, tmp; + + a = lhs.significand >> 32; + b = lhs.significand & 0xffffffff; + c = rhs.significand >> 32; + d = rhs.significand & 0xffffffff; + + ac = a * c; + bc = b * c; + ad = a * d; + bd = b * d; + + tmp = (bd >> 32) + (ad & 0xffffffff) + (bc & 0xffffffff); + + /* mult_round. */ + + tmp += 1U << 31; + + return lexbor_diyfp(ac + (ad >> 32) + (bc >> 32) + (tmp >> 32), + lhs.exp + rhs.exp + 64); +#endif +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_normalize(lexbor_diyfp_t v) +{ + return lexbor_diyfp_shift_left(v, + (unsigned) lexbor_diyfp_leading_zeros64(v.significand)); +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_DIYFP_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.c new file mode 100644 index 0000000..0f5468f --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2018-2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/dobject.h" + + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + #include +#endif + + +lexbor_dobject_t * +lexbor_dobject_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_dobject_t)); +} + +lxb_status_t +lexbor_dobject_init(lexbor_dobject_t *dobject, + size_t chunk_size, size_t struct_size) +{ + lxb_status_t status; + + if (dobject == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (chunk_size == 0 || struct_size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + /* Set params */ + dobject->allocated = 0UL; + dobject->struct_size = struct_size; + + /* Init memory */ + dobject->mem = lexbor_mem_create(); + + status = lexbor_mem_init(dobject->mem, + lexbor_mem_align(chunk_size * dobject->struct_size)); + if (status) { + return status; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(dobject->mem->chunk->data, + dobject->mem->chunk->size); +#endif + + /* Array */ + dobject->cache = lexbor_array_create(); + + status = lexbor_array_init(dobject->cache, chunk_size); + if (status) + return status; + + return LXB_STATUS_OK; +} + +void +lexbor_dobject_clean(lexbor_dobject_t *dobject) +{ + if (dobject != NULL) { + dobject->allocated = 0UL; + + lexbor_mem_clean(dobject->mem); + lexbor_array_clean(dobject->cache); + } +} + +lexbor_dobject_t * +lexbor_dobject_destroy(lexbor_dobject_t *dobject, bool destroy_self) +{ + if (dobject == NULL) + return NULL; + + dobject->mem = lexbor_mem_destroy(dobject->mem, true); + dobject->cache = lexbor_array_destroy(dobject->cache, true); + + if (destroy_self == true) { + return lexbor_free(dobject); + } + + return dobject; +} + +void * +lexbor_dobject_alloc(lexbor_dobject_t *dobject) +{ + void *data; + + if (lexbor_array_length(dobject->cache) != 0) { + dobject->allocated++; + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + data = lexbor_array_pop(dobject->cache); + ASAN_UNPOISON_MEMORY_REGION(data, dobject->struct_size); + + return data; +#else + return lexbor_array_pop(dobject->cache); +#endif + } + + data = lexbor_mem_alloc(dobject->mem, dobject->struct_size); + if (data == NULL) { + return NULL; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_UNPOISON_MEMORY_REGION(data, dobject->struct_size); +#endif + + dobject->allocated++; + + return data; +} + +void * +lexbor_dobject_calloc(lexbor_dobject_t *dobject) +{ + void *data = lexbor_dobject_alloc(dobject); + + if (data != NULL) { + memset(data, 0, dobject->struct_size); + } + + return data; +} + +void * +lexbor_dobject_free(lexbor_dobject_t *dobject, void *data) +{ + if (data == NULL) { + return NULL; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(data, dobject->struct_size); +#endif + + if (lexbor_array_push(dobject->cache, data) == LXB_STATUS_OK) { + dobject->allocated--; + return NULL; + } + + return data; +} + +void * +lexbor_dobject_by_absolute_position(lexbor_dobject_t *dobject, size_t pos) +{ + size_t chunk_idx, chunk_pos, i; + lexbor_mem_chunk_t *chunk; + + if (pos >= dobject->allocated) { + return NULL; + } + + chunk = dobject->mem->chunk_first; + chunk_pos = pos * dobject->struct_size; + chunk_idx = chunk_pos / dobject->mem->chunk_min_size; + + for (i = 0; i < chunk_idx; i++) { + chunk = chunk->next; + } + + return &chunk->data[chunk_pos % chunk->size]; +} + +/* + * No inline functions for ABI. + */ +size_t +lexbor_dobject_allocated_noi(lexbor_dobject_t *dobject) +{ + return lexbor_dobject_allocated(dobject); +} + +size_t +lexbor_dobject_cache_length_noi(lexbor_dobject_t *dobject) +{ + return lexbor_dobject_cache_length(dobject); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.h new file mode 100644 index 0000000..69497fd --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dobject.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_DOBJECT_H +#define LEXBOR_DOBJECT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" +#include "lexbor/core/mem.h" +#include "lexbor/core/array.h" + + +typedef struct { + lexbor_mem_t *mem; + lexbor_array_t *cache; + + size_t allocated; + size_t struct_size; +} +lexbor_dobject_t; + + +LXB_API lexbor_dobject_t * +lexbor_dobject_create(void); + +LXB_API lxb_status_t +lexbor_dobject_init(lexbor_dobject_t *dobject, + size_t chunk_size, size_t struct_size); + +LXB_API void +lexbor_dobject_clean(lexbor_dobject_t *dobject); + +LXB_API lexbor_dobject_t * +lexbor_dobject_destroy(lexbor_dobject_t *dobject, bool destroy_self); + + +LXB_API void * +lexbor_dobject_alloc(lexbor_dobject_t *dobject); + +LXB_API void * +lexbor_dobject_calloc(lexbor_dobject_t *dobject); + +LXB_API void * +lexbor_dobject_free(lexbor_dobject_t *dobject, void *data); + + +LXB_API void * +lexbor_dobject_by_absolute_position(lexbor_dobject_t *dobject, size_t pos); + + +/* + * Inline functions + */ +lxb_inline size_t +lexbor_dobject_allocated(lexbor_dobject_t *dobject) +{ + return dobject->allocated; +} + +lxb_inline size_t +lexbor_dobject_cache_length(lexbor_dobject_t *dobject) +{ + return lexbor_array_length(dobject->cache); +} + +/* + * No inline functions for ABI. + */ +LXB_API size_t +lexbor_dobject_allocated_noi(lexbor_dobject_t *dobject); + +LXB_API size_t +lexbor_dobject_cache_length_noi(lexbor_dobject_t *dobject); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_DOBJECT_H */ + + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.c new file mode 100644 index 0000000..7da7ce0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.c @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + */ + +#include "lexbor/core/str.h" +#include "lexbor/core/diyfp.h" +#include "lexbor/core/dtoa.h" + +#include +#include + + +lxb_inline void +lexbor_grisu2_round(lxb_char_t *start, size_t len, uint64_t delta, uint64_t rest, + uint64_t ten_kappa, uint64_t wp_w) +{ + while (rest < wp_w && delta - rest >= ten_kappa + && (rest + ten_kappa < wp_w || /* closer */ + wp_w - rest > rest + ten_kappa - wp_w)) + { + start[len - 1]--; + rest += ten_kappa; + } +} + +lxb_inline int +lexbor_dec_count(uint32_t n) +{ + if (n < 10) return 1; + if (n < 100) return 2; + if (n < 1000) return 3; + if (n < 10000) return 4; + if (n < 100000) return 5; + if (n < 1000000) return 6; + if (n < 10000000) return 7; + if (n < 100000000) return 8; + if (n < 1000000000) return 9; + + return 10; +} + +lxb_inline size_t +lexbor_grisu2_gen(lexbor_diyfp_t W, lexbor_diyfp_t Mp, uint64_t delta, + lxb_char_t *begin, lxb_char_t *end, int *dec_exp) +{ + int kappa; + lxb_char_t c, *p; + uint32_t p1, d; + uint64_t p2, tmp; + lexbor_diyfp_t one, wp_w; + + static const uint64_t pow10[] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000 + }; + + wp_w = lexbor_diyfp_sub(Mp, W); + + one = lexbor_diyfp((uint64_t) 1 << -Mp.exp, Mp.exp); + p1 = (uint32_t) (Mp.significand >> -one.exp); + p2 = Mp.significand & (one.significand - 1); + + p = begin; + + /* GCC 4.2 complains about uninitialized d. */ + d = 0; + + kappa = lexbor_dec_count(p1); + + while (kappa > 0) { + switch (kappa) { + case 10: d = p1 / 1000000000; p1 %= 1000000000; break; + case 9: d = p1 / 100000000; p1 %= 100000000; break; + case 8: d = p1 / 10000000; p1 %= 10000000; break; + case 7: d = p1 / 1000000; p1 %= 1000000; break; + case 6: d = p1 / 100000; p1 %= 100000; break; + case 5: d = p1 / 10000; p1 %= 10000; break; + case 4: d = p1 / 1000; p1 %= 1000; break; + case 3: d = p1 / 100; p1 %= 100; break; + case 2: d = p1 / 10; p1 %= 10; break; + case 1: d = p1; p1 = 0; break; + default: + /* Never go here. */ + return 0; + } + + if (d != 0 || p != begin) { + *p = '0' + d; + + p += 1; + if (p == end) { + return p - begin; + } + } + + kappa--; + + tmp = ((uint64_t) p1 << -one.exp) + p2; + + if (tmp <= delta) { + *dec_exp += kappa; + lexbor_grisu2_round(begin, p - begin, delta, tmp, + pow10[kappa] << -one.exp, wp_w.significand); + return p - begin; + } + } + + /* kappa = 0. */ + + for ( ;; ) { + p2 *= 10; + delta *= 10; + c = (char) (p2 >> -one.exp); + + if (c != 0 || p != begin) { + *p = '0' + c; + + p += 1; + if (p == end) { + return p - begin; + } + } + + p2 &= one.significand - 1; + kappa--; + + if (p2 < delta) { + *dec_exp += kappa; + tmp = (-kappa < 10) ? pow10[-kappa] : 0; + lexbor_grisu2_round(begin, p - begin, delta, p2, one.significand, + wp_w.significand * tmp); + break; + } + } + + return p - begin; +} + +lxb_inline lexbor_diyfp_t +lexbor_diyfp_normalize_boundary(lexbor_diyfp_t v) +{ + while ((v.significand & (LEXBOR_DBL_HIDDEN_BIT << 1)) == 0) { + v.significand <<= 1; + v.exp--; + } + + return lexbor_diyfp_shift_left(v, LEXBOR_SIGNIFICAND_SHIFT - 2); +} + +lxb_inline void +lexbor_diyfp_normalize_boundaries(lexbor_diyfp_t v, lexbor_diyfp_t* minus, + lexbor_diyfp_t* plus) +{ + lexbor_diyfp_t pl, mi; + + pl = lexbor_diyfp_normalize_boundary(lexbor_diyfp((v.significand << 1) + 1, + v.exp - 1)); + + if (v.significand == LEXBOR_DBL_HIDDEN_BIT) { + mi = lexbor_diyfp((v.significand << 2) - 1, v.exp - 2); + + } else { + mi = lexbor_diyfp((v.significand << 1) - 1, v.exp - 1); + } + + mi.significand <<= mi.exp - pl.exp; + mi.exp = pl.exp; + + *plus = pl; + *minus = mi; +} + +lxb_inline size_t +lexbor_grisu2(double value, lxb_char_t *begin, lxb_char_t *end, int *dec_exp) +{ + lexbor_diyfp_t v, w_m, w_p, c_mk, W, Wp, Wm; + + v = lexbor_diyfp_from_d2(value); + + lexbor_diyfp_normalize_boundaries(v, &w_m, &w_p); + + c_mk = lexbor_cached_power_bin(w_p.exp, dec_exp); + W = lexbor_diyfp_mul(lexbor_diyfp_normalize(v), c_mk); + + Wp = lexbor_diyfp_mul(w_p, c_mk); + Wm = lexbor_diyfp_mul(w_m, c_mk); + + Wm.significand++; + Wp.significand--; + + return lexbor_grisu2_gen(W, Wp, Wp.significand - Wm.significand, begin, end, + dec_exp); +} + +lxb_inline size_t +lexbor_write_exponent(int exp, lxb_char_t *begin, lxb_char_t *end) +{ + char *p; + size_t len; + uint32_t u32; + char buf[4]; + + /* -324 <= exp <= 308. */ + + if ((begin + (sizeof(buf) - 1) + 1) >= end) { + return 0; + } + + if (exp < 0) { + *begin = '-'; + begin += 1; + + exp = -exp; + } + else { + *begin++ = '+'; + } + + u32 = exp; + p = buf + (sizeof(buf) - 1); + + do { + *--p = u32 % 10 + '0'; + u32 /= 10; + } + while (u32 != 0); + + len = buf + (sizeof(buf) - 1) - p; + + memcpy(begin, p, len); + + return len + 1; +} + +lxb_inline size_t +lexbor_prettify(lxb_char_t *begin, lxb_char_t *end, size_t len, int dec_exp) +{ + int kk, offset, length; + size_t size; + + /* 10^(kk-1) <= v < 10^kk */ + + length = (int) len; + kk = length + dec_exp; + + if (length <= kk && kk <= 21) { + /* 1234e7 -> 12340000000 */ + + if (kk - length > 0) { + if ((&begin[length] + (kk - length)) < end) { + memset(&begin[length], '0', kk - length); + } + else { + memset(&begin[length], '0', (end - &begin[length])); + } + } + + return kk; + } + else if (0 < kk && kk <= 21) { + /* 1234e-2 -> 12.34 */ + + if ((&begin[kk + 1] + (length - kk)) >= end) { + return length; + } + + memmove(&begin[kk + 1], &begin[kk], length - kk); + begin[kk] = '.'; + + return (length + 1); + } + else if (-6 < kk && kk <= 0) { + /* 1234e-6 -> 0.001234 */ + + offset = 2 - kk; + if ((&begin[offset] + length) >= end + || (begin + 2) >= end) + { + return length; + } + + memmove(&begin[offset], begin, length); + begin[0] = '0'; + begin[1] = '.'; + + if (offset - 2 > 0) { + if ((&begin[2] + (offset - 2)) >= end) { + return length; + } + + memset(&begin[2], '0', offset - 2); + } + + return (length + offset); + } + else if (length == 1) { + /* 1e30 */ + + if ((begin + 1) >= end) { + return length; + } + + begin[1] = 'e'; + + size = lexbor_write_exponent(kk - 1, &begin[2], end); + + return (size + 2); + } + + /* 1234e30 -> 1.234e33 */ + + if ((&begin[2] + (length - 1)) >= end) { + return length; + } + + memmove(&begin[2], &begin[1], length - 1); + begin[1] = '.'; + begin[length + 1] = 'e'; + + size = lexbor_write_exponent(kk - 1, &begin[length + 2], end); + + return (size + length + 2); +} + +size_t +lexbor_dtoa(double value, lxb_char_t *begin, size_t len) +{ + int dec_exp, minus; + size_t length; + lxb_char_t *end = begin + len; + + if (len == 0) { + return 0; + } + + /* Not handling NaN and inf. */ + + minus = 0; + + if (value == 0) { + *begin = '0'; + + return 1; + } + + if (signbit(value)) { + *begin = '-'; + + begin += 1; + if (begin == end) { + return 1; + } + + value = -value; + minus = 1; + } + + length = lexbor_grisu2(value, begin, end, &dec_exp); + length = lexbor_prettify(begin, end, length, dec_exp); + + return (minus + length); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.h new file mode 100644 index 0000000..2d569cf --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/dtoa.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + */ + +#ifndef LEXBOR_DTOA_H +#define LEXBOR_DTOA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +LXB_API size_t +lexbor_dtoa(double value, lxb_char_t *begin, size_t len); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_DTOA_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/fs.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/fs.h new file mode 100644 index 0000000..1b64c6d --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/fs.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_FS_H +#define LEXBOR_FS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +typedef lexbor_action_t (*lexbor_fs_dir_file_f)(const lxb_char_t *fullpath, + size_t fullpath_len, + const lxb_char_t *filename, + size_t filename_len, void *ctx); + +typedef int lexbor_fs_dir_opt_t; + +enum lexbor_fs_dir_opt { + LEXBOR_FS_DIR_OPT_UNDEF = 0x00, + LEXBOR_FS_DIR_OPT_WITHOUT_DIR = 0x01, + LEXBOR_FS_DIR_OPT_WITHOUT_FILE = 0x02, + LEXBOR_FS_DIR_OPT_WITHOUT_HIDDEN = 0x04, +}; + +typedef enum { + LEXBOR_FS_FILE_TYPE_UNDEF = 0x00, + LEXBOR_FS_FILE_TYPE_FILE = 0x01, + LEXBOR_FS_FILE_TYPE_DIRECTORY = 0x02, + LEXBOR_FS_FILE_TYPE_BLOCK_DEVICE = 0x03, + LEXBOR_FS_FILE_TYPE_CHARACTER_DEVICE = 0x04, + LEXBOR_FS_FILE_TYPE_PIPE = 0x05, + LEXBOR_FS_FILE_TYPE_SYMLINK = 0x06, + LEXBOR_FS_FILE_TYPE_SOCKET = 0x07 +} +lexbor_fs_file_type_t; + + +LXB_API lxb_status_t +lexbor_fs_dir_read(const lxb_char_t *dirpath, lexbor_fs_dir_opt_t opt, + lexbor_fs_dir_file_f callback, void *ctx); + +LXB_API lexbor_fs_file_type_t +lexbor_fs_file_type(const lxb_char_t *full_path); + +LXB_API lxb_char_t * +lexbor_fs_file_easy_read(const lxb_char_t *full_path, size_t *len); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_FS_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.c new file mode 100644 index 0000000..c16466e --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.c @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2019-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#define LEXBOR_HASH_EXTERN +#include "lexbor/core/hash.h" +#undef LEXBOR_HASH_EXTERN + +#include "lexbor/core/str.h" + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_lowercase[256]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_uppercase[256]; +#endif + + +/* Insert variable. */ +const lexbor_hash_insert_t lexbor_hash_insert_var = { + .hash = lexbor_hash_make_id, + .copy = lexbor_hash_copy, + .cmp = lexbor_str_data_ncmp +}; + +const lexbor_hash_insert_t lexbor_hash_insert_lower_var = { + .hash = lexbor_hash_make_id_lower, + .copy = lexbor_hash_copy_lower, + .cmp = lexbor_str_data_nlocmp_right +}; + +const lexbor_hash_insert_t lexbor_hash_insert_upper_var = { + .hash = lexbor_hash_make_id_upper, + .copy = lexbor_hash_copy_upper, + .cmp = lexbor_str_data_nupcmp_right +}; + +LXB_API const lexbor_hash_insert_t +*lexbor_hash_insert_raw = &lexbor_hash_insert_var; + +LXB_API const lexbor_hash_insert_t +*lexbor_hash_insert_lower = &lexbor_hash_insert_lower_var; + +LXB_API const lexbor_hash_insert_t +*lexbor_hash_insert_upper = &lexbor_hash_insert_upper_var; + +/* Search variable. */ +const lexbor_hash_search_t lexbor_hash_search_var = { + .hash = lexbor_hash_make_id, + .cmp = lexbor_str_data_ncmp +}; + +const lexbor_hash_search_t lexbor_hash_search_lower_var = { + .hash = lexbor_hash_make_id_lower, + .cmp = lexbor_str_data_nlocmp_right +}; + +const lexbor_hash_search_t lexbor_hash_search_upper_var = { + .hash = lexbor_hash_make_id_upper, + .cmp = lexbor_str_data_nupcmp_right +}; + +LXB_API const lexbor_hash_search_t +*lexbor_hash_search_raw = &lexbor_hash_search_var; + +LXB_API const lexbor_hash_search_t +*lexbor_hash_search_lower = &lexbor_hash_search_lower_var; + +LXB_API const lexbor_hash_search_t +*lexbor_hash_search_upper = &lexbor_hash_search_upper_var; + + +lxb_inline lexbor_hash_entry_t ** +lexbor_hash_table_create(lexbor_hash_t *hash) +{ + return lexbor_calloc(hash->table_size, sizeof(lexbor_hash_entry_t *)); +} + +lxb_inline void +lexbor_hash_table_clean(lexbor_hash_t *hash) +{ + memset(hash->table, 0, sizeof(lexbor_hash_t *) * hash->table_size); +} + +lxb_inline lexbor_hash_entry_t ** +lexbor_hash_table_destroy(lexbor_hash_t *hash) +{ + if (hash->table != NULL) { + return lexbor_free(hash->table); + } + + return NULL; +} + +lxb_inline lexbor_hash_entry_t * +_lexbor_hash_entry_create(lexbor_hash_t *hash, const lexbor_hash_copy_f copy_func, + const lxb_char_t *key, size_t length) +{ + lexbor_hash_entry_t *entry = lexbor_dobject_calloc(hash->entries); + if (entry == NULL) { + return NULL; + } + + entry->length = length; + + if (copy_func(hash, entry, key, length) != LXB_STATUS_OK) { + lexbor_dobject_free(hash->entries, entry); + return NULL; + } + + return entry; +} + +lexbor_hash_t * +lexbor_hash_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_hash_t)); +} + +lxb_status_t +lexbor_hash_init(lexbor_hash_t *hash, size_t table_size, size_t struct_size) +{ + lxb_status_t status; + size_t chunk_size; + + if (hash == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (table_size < LEXBOR_HASH_TABLE_MIN_SIZE) { + table_size = LEXBOR_HASH_TABLE_MIN_SIZE; + } + + chunk_size = table_size / 2; + + hash->table_size = table_size; + + hash->entries = lexbor_dobject_create(); + status = lexbor_dobject_init(hash->entries, chunk_size, struct_size); + if (status != LXB_STATUS_OK) { + return status; + } + + hash->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(hash->mraw, chunk_size * 12); + if (status != LXB_STATUS_OK) { + return status; + } + + hash->table = lexbor_hash_table_create(hash); + if (hash->table == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + hash->struct_size = struct_size; + + return LXB_STATUS_OK; +} + +void +lexbor_hash_clean(lexbor_hash_t *hash) +{ + lexbor_dobject_clean(hash->entries); + lexbor_mraw_clean(hash->mraw); + lexbor_hash_table_clean(hash); +} + +lexbor_hash_t * +lexbor_hash_destroy(lexbor_hash_t *hash, bool destroy_obj) +{ + if (hash == NULL) { + return NULL; + } + + hash->entries = lexbor_dobject_destroy(hash->entries, true); + hash->mraw = lexbor_mraw_destroy(hash->mraw, true); + hash->table = lexbor_hash_table_destroy(hash); + + if (destroy_obj) { + return lexbor_free(hash); + } + + return hash; +} + +void * +lexbor_hash_insert(lexbor_hash_t *hash, const lexbor_hash_insert_t *insert, + const lxb_char_t *key, size_t length) +{ + lxb_char_t *str; + uint32_t hash_id, table_idx; + lexbor_hash_entry_t *entry; + + hash_id = insert->hash(key, length); + table_idx = hash_id % hash->table_size; + + entry = hash->table[table_idx]; + + if (entry == NULL) { + entry = _lexbor_hash_entry_create(hash, insert->copy, key, length); + hash->table[table_idx] = entry; + + return entry; + } + + do { + str = lexbor_hash_entry_str(entry); + + if (entry->length == length && insert->cmp(str, key, length)) { + return entry; + } + + if (entry->next == NULL) { + break; + } + + entry = entry->next; + } + while (1); + + entry->next = _lexbor_hash_entry_create(hash, insert->copy, key, length); + + return entry->next; +} + +void * +lexbor_hash_insert_by_entry(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length) +{ + lxb_char_t *str; + uint32_t hash_id, table_idx; + lexbor_hash_entry_t *item; + + hash_id = search->hash(key, length); + table_idx = hash_id % hash->table_size; + + item = hash->table[table_idx]; + + if (item == NULL) { + hash->table[table_idx] = entry; + + return entry; + } + + do { + str = lexbor_hash_entry_str(item); + + if (item->length == length && search->cmp(str, key, length)) { + return item; + } + + if (item->next == NULL) { + break; + } + + item = item->next; + } + while (1); + + item->next = entry; + + return entry; +} + +void +lexbor_hash_remove(lexbor_hash_t *hash, const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length) +{ + lexbor_hash_remove_by_hash_id(hash, search->hash(key, length), + key, length, search->cmp); +} + +void * +lexbor_hash_search(lexbor_hash_t *hash, const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length) +{ + return lexbor_hash_search_by_hash_id(hash, search->hash(key, length), + key, length, search->cmp); +} + +void +lexbor_hash_remove_by_hash_id(lexbor_hash_t *hash, uint32_t hash_id, + const lxb_char_t *key, size_t length, + const lexbor_hash_cmp_f cmp_func) +{ + uint32_t table_idx; + lxb_char_t *str; + lexbor_hash_entry_t *entry, *prev; + + table_idx = hash_id % hash->table_size; + entry = hash->table[table_idx]; + prev = NULL; + + while (entry != NULL) { + str = lexbor_hash_entry_str(entry); + + if (entry->length == length && cmp_func(str, key, length)) { + if (prev == NULL) { + hash->table[table_idx] = entry->next; + } + else { + prev->next = entry->next; + } + + if (length > LEXBOR_HASH_SHORT_SIZE) { + lexbor_mraw_free(hash->mraw, entry->u.long_str); + } + + lexbor_dobject_free(hash->entries, entry); + + return; + } + + prev = entry; + entry = entry->next; + } +} + +void * +lexbor_hash_search_by_hash_id(lexbor_hash_t *hash, uint32_t hash_id, + const lxb_char_t *key, size_t length, + const lexbor_hash_cmp_f cmp_func) +{ + lxb_char_t *str; + lexbor_hash_entry_t *entry; + + entry = hash->table[ hash_id % hash->table_size ]; + + while (entry != NULL) { + str = lexbor_hash_entry_str(entry); + + if (entry->length == length && cmp_func(str, key, length)) { + return entry; + } + + entry = entry->next; + } + + return NULL; +} + +uint32_t +lexbor_hash_make_id(const lxb_char_t *key, size_t length) +{ + size_t i; + uint32_t hash_id; + + for (i = hash_id = 0; i < length; i++) { + hash_id += key[i]; + hash_id += (hash_id << 10); + hash_id ^= (hash_id >> 6); + } + + hash_id += (hash_id << 3); + hash_id ^= (hash_id >> 11); + hash_id += (hash_id << 15); + + return hash_id; +} + +uint32_t +lexbor_hash_make_id_lower(const lxb_char_t *key, size_t length) +{ + size_t i; + uint32_t hash_id; + + for (i = hash_id = 0; i < length; i++) { + hash_id += lexbor_str_res_map_lowercase[ key[i] ]; + hash_id += (hash_id << 10); + hash_id ^= (hash_id >> 6); + } + + hash_id += (hash_id << 3); + hash_id ^= (hash_id >> 11); + hash_id += (hash_id << 15); + + return hash_id; +} + +uint32_t +lexbor_hash_make_id_upper(const lxb_char_t *key, size_t length) +{ + size_t i; + uint32_t hash_id; + + for (i = hash_id = 0; i < length; i++) { + hash_id += lexbor_str_res_map_uppercase[ key[i] ]; + hash_id += (hash_id << 10); + hash_id ^= (hash_id >> 6); + } + + hash_id += (hash_id << 3); + hash_id ^= (hash_id >> 11); + hash_id += (hash_id << 15); + + return hash_id; +} + +lxb_status_t +lexbor_hash_copy(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length) +{ + lxb_char_t *to; + + if (length <= LEXBOR_HASH_SHORT_SIZE) { + to = entry->u.short_str; + } + else { + entry->u.long_str = lexbor_mraw_alloc(hash->mraw, length + 1); + if (entry->u.long_str == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + to = entry->u.long_str; + } + + memcpy(to, key, length); + + to[length] = '\0'; + + return LXB_STATUS_OK; +} + +lxb_status_t +lexbor_hash_copy_lower(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length) +{ + lxb_char_t *to; + + if (length <= LEXBOR_HASH_SHORT_SIZE) { + to = entry->u.short_str; + } + else { + entry->u.long_str = lexbor_mraw_alloc(hash->mraw, length + 1); + if (entry->u.long_str == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + to = entry->u.long_str; + } + + for (size_t i = 0; i < length; i++) { + to[i] = lexbor_str_res_map_lowercase[ key[i] ]; + } + + to[length] = '\0'; + + return LXB_STATUS_OK; +} + +lxb_status_t +lexbor_hash_copy_upper(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length) +{ + lxb_char_t *to; + + if (length <= LEXBOR_HASH_SHORT_SIZE) { + to = entry->u.short_str; + } + else { + entry->u.long_str = lexbor_mraw_alloc(hash->mraw, length + 1); + if (entry->u.long_str == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + to = entry->u.long_str; + } + + for (size_t i = 0; i < length; i++) { + to[i] = lexbor_str_res_map_uppercase[ key[i] ]; + } + + to[length] = '\0'; + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.h new file mode 100644 index 0000000..0647617 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/hash.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_HASH_H +#define LEXBOR_HASH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/dobject.h" +#include "lexbor/core/mraw.h" + + +#define LEXBOR_HASH_SHORT_SIZE 16 +#define LEXBOR_HASH_TABLE_MIN_SIZE 32 + + +typedef struct lexbor_hash_search lexbor_hash_search_t; +typedef struct lexbor_hash_insert lexbor_hash_insert_t; + +#ifndef LEXBOR_HASH_EXTERN +LXB_EXTERN const lexbor_hash_insert_t *lexbor_hash_insert_raw; +LXB_EXTERN const lexbor_hash_insert_t *lexbor_hash_insert_lower; +LXB_EXTERN const lexbor_hash_insert_t *lexbor_hash_insert_upper; + +LXB_EXTERN const lexbor_hash_search_t *lexbor_hash_search_raw; +LXB_EXTERN const lexbor_hash_search_t *lexbor_hash_search_lower; +LXB_EXTERN const lexbor_hash_search_t *lexbor_hash_search_upper; +#endif + +/* + * FIXME: + * It is necessary to add the rebuild of a hash table + * and optimize collisions. + */ + +typedef struct lexbor_hash lexbor_hash_t; +typedef struct lexbor_hash_entry lexbor_hash_entry_t; + +typedef uint32_t +(*lexbor_hash_id_f)(const lxb_char_t *key, size_t size); + +typedef lxb_status_t +(*lexbor_hash_copy_f)(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t size); + +typedef bool +(*lexbor_hash_cmp_f)(const lxb_char_t *first, + const lxb_char_t *second, size_t size); + +struct lexbor_hash_entry { + union { + lxb_char_t *long_str; + lxb_char_t short_str[LEXBOR_HASH_SHORT_SIZE + 1]; + } u; + + size_t length; + + lexbor_hash_entry_t *next; +}; + +struct lexbor_hash { + lexbor_dobject_t *entries; + lexbor_mraw_t *mraw; + + lexbor_hash_entry_t **table; + size_t table_size; + + size_t struct_size; +}; + +struct lexbor_hash_insert { + lexbor_hash_id_f hash; /* For generate a hash id. */ + lexbor_hash_cmp_f cmp; /* For compare key. */ + lexbor_hash_copy_f copy; /* For copy key. */ +}; + +struct lexbor_hash_search { + lexbor_hash_id_f hash; /* For generate a hash id. */ + lexbor_hash_cmp_f cmp; /* For compare key. */ +}; + + +LXB_API lexbor_hash_t * +lexbor_hash_create(void); + +LXB_API lxb_status_t +lexbor_hash_init(lexbor_hash_t *hash, size_t table_size, size_t struct_size); + +LXB_API void +lexbor_hash_clean(lexbor_hash_t *hash); + +LXB_API lexbor_hash_t * +lexbor_hash_destroy(lexbor_hash_t *hash, bool destroy_obj); + + +LXB_API void * +lexbor_hash_insert(lexbor_hash_t *hash, const lexbor_hash_insert_t *insert, + const lxb_char_t *key, size_t length); + +LXB_API void * +lexbor_hash_insert_by_entry(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length); + +LXB_API void +lexbor_hash_remove(lexbor_hash_t *hash, const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length); + +LXB_API void * +lexbor_hash_search(lexbor_hash_t *hash, const lexbor_hash_search_t *search, + const lxb_char_t *key, size_t length); + +LXB_API void +lexbor_hash_remove_by_hash_id(lexbor_hash_t *hash, uint32_t hash_id, + const lxb_char_t *key, size_t length, + const lexbor_hash_cmp_f cmp_func); + +LXB_API void * +lexbor_hash_search_by_hash_id(lexbor_hash_t *hash, uint32_t hash_id, + const lxb_char_t *key, size_t length, + const lexbor_hash_cmp_f cmp_func); + + +LXB_API uint32_t +lexbor_hash_make_id(const lxb_char_t *key, size_t length); + +LXB_API uint32_t +lexbor_hash_make_id_lower(const lxb_char_t *key, size_t length); + +LXB_API uint32_t +lexbor_hash_make_id_upper(const lxb_char_t *key, size_t length); + +LXB_API lxb_status_t +lexbor_hash_copy(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length); + +LXB_API lxb_status_t +lexbor_hash_copy_lower(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length); + +LXB_API lxb_status_t +lexbor_hash_copy_upper(lexbor_hash_t *hash, lexbor_hash_entry_t *entry, + const lxb_char_t *key, size_t length); + + +/* + * Inline functions + */ +lxb_inline lexbor_mraw_t * +lexbor_hash_mraw(const lexbor_hash_t *hash) +{ + return hash->mraw; +} + +lxb_inline lxb_char_t * +lexbor_hash_entry_str(const lexbor_hash_entry_t *entry) +{ + if (entry->length <= LEXBOR_HASH_SHORT_SIZE) { + return (lxb_char_t *) entry->u.short_str; + } + + return entry->u.long_str; +} + +lxb_inline lxb_char_t * +lexbor_hash_entry_str_set(lexbor_hash_entry_t *entry, + lxb_char_t *data, size_t length) +{ + entry->length = length; + + if (length <= LEXBOR_HASH_SHORT_SIZE) { + memcpy(entry->u.short_str, data, length); + return (lxb_char_t *) entry->u.short_str; + } + + entry->u.long_str = data; + return entry->u.long_str; +} + +lxb_inline void +lexbor_hash_entry_str_free(lexbor_hash_t *hash, lexbor_hash_entry_t *entry) +{ + if (entry->length > LEXBOR_HASH_SHORT_SIZE) { + lexbor_mraw_free(hash->mraw, entry->u.long_str); + } + + entry->length = 0; +} + +lxb_inline lexbor_hash_entry_t * +lexbor_hash_entry_create(lexbor_hash_t *hash) +{ + return (lexbor_hash_entry_t *) lexbor_dobject_calloc(hash->entries); +} + +lxb_inline lexbor_hash_entry_t * +lexbor_hash_entry_destroy(lexbor_hash_t *hash, lexbor_hash_entry_t *entry) +{ + return (lexbor_hash_entry_t *) lexbor_dobject_free(hash->entries, entry); +} + +lxb_inline size_t +lexbor_hash_entries_count(lexbor_hash_t *hash) +{ + return lexbor_dobject_allocated(hash->entries); +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_HASH_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.c new file mode 100644 index 0000000..363256b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.c @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/in.h" + + +lexbor_in_t * +lexbor_in_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_in_t)); +} + +lxb_status_t +lexbor_in_init(lexbor_in_t *incoming, size_t chunk_size) +{ + if (incoming == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (chunk_size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + incoming->nodes = lexbor_dobject_create(); + + return lexbor_dobject_init(incoming->nodes, chunk_size, + sizeof(lexbor_in_node_t)); +} + +void +lexbor_in_clean(lexbor_in_t *incoming) +{ + lexbor_dobject_clean(incoming->nodes); +} + +lexbor_in_t * +lexbor_in_destroy(lexbor_in_t *incoming, bool self_destroy) +{ + if (incoming == NULL) { + return NULL; + } + + incoming->nodes = lexbor_dobject_destroy(incoming->nodes, true); + + if (self_destroy == true) { + return lexbor_free(incoming); + } + + return incoming; +} + +lexbor_in_node_t * +lexbor_in_node_make(lexbor_in_t *incoming, lexbor_in_node_t *last_node, + const lxb_char_t *buf, size_t buf_size) +{ + lexbor_in_node_t *node = lexbor_dobject_alloc(incoming->nodes); + + if (node == NULL) { + return NULL; + } + + node->opt = LEXBOR_IN_OPT_UNDEF; + node->begin = buf; + node->end = buf ? (buf + buf_size) : NULL; + node->use = buf; + + if (last_node != NULL) { + last_node->next = node; + node->offset = last_node->offset + (last_node->end - last_node->begin); + } + else { + node->offset = 0; + } + + node->prev = last_node; + node->next = NULL; + node->incoming = incoming; + + return node; +} + +void +lexbor_in_node_clean(lexbor_in_node_t *node) +{ + lexbor_in_t *incoming = node->incoming; + + memset(node, 0, sizeof(lexbor_in_node_t)); + + node->incoming = incoming; +} + +lexbor_in_node_t * +lexbor_in_node_destroy(lexbor_in_t *incoming, + lexbor_in_node_t *node, bool self_destroy) +{ + if (node == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_dobject_free(incoming->nodes, node); + } + + return node; +} + +lexbor_in_node_t * +lexbor_in_node_split(lexbor_in_node_t *node, const lxb_char_t *pos) +{ + lexbor_in_node_t *new_node; + + new_node = lexbor_dobject_alloc(node->incoming->nodes); + + if (new_node == NULL) { + return NULL; + } + + new_node->offset = node->offset + (pos - node->begin); + new_node->opt = LEXBOR_IN_OPT_UNDEF; + new_node->begin = pos; + new_node->end = node->end; + new_node->next = NULL; + new_node->prev = node; + new_node->incoming = node->incoming; + + node->end = pos; + node->next = new_node; + + if (node->use > pos) { + new_node->use = node->use; + node->use = pos; + } + else { + new_node->use = pos; + } + + return new_node; +} + +lexbor_in_node_t * +lexbor_in_node_find(lexbor_in_node_t *node, const lxb_char_t *pos) +{ + while (node->next) { + node = node->next; + } + + while (node && (node->begin > pos || node->end < pos)) { + node = node->prev; + } + + return node; +} + +const lxb_char_t * +lexbor_in_node_pos_up(lexbor_in_node_t *node, lexbor_in_node_t **return_node, + const lxb_char_t *pos, size_t offset) +{ + do { + pos = pos + offset; + + if (node->end >= pos) { + if (return_node != NULL && *return_node != node) { + *return_node = node; + } + + return pos; + } + + if (node->next == NULL) { + if (return_node != NULL && *return_node != node) { + *return_node = node; + } + + return node->end; + } + + offset = pos - node->end; + node = node->next; + pos = node->begin; + + } + while (1); + + return NULL; +} + +const lxb_char_t * +lexbor_in_node_pos_down(lexbor_in_node_t *node, lexbor_in_node_t **return_node, + const lxb_char_t *pos, size_t offset) +{ + do { + pos = pos - offset; + + if (node->begin <= pos) { + if (return_node != NULL && *return_node != node) { + *return_node = node; + } + + return pos; + } + + if (node->prev == NULL) { + if (return_node != NULL && *return_node != node) { + *return_node = node; + } + + return node->begin; + } + + offset = node->begin - pos; + node = node->prev; + pos = node->end; + + } + while (1); + + return NULL; +} + +/* +* No inline functions for ABI. +*/ +const lxb_char_t * +lexbor_in_node_begin_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_begin(node); +} + +const lxb_char_t * +lexbor_in_node_end_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_end(node); +} + +size_t +lexbor_in_node_offset_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_offset(node); +} + +lexbor_in_node_t * +lexbor_in_node_next_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_next(node); +} + +lexbor_in_node_t * +lexbor_in_node_prev_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_prev(node); +} + +lexbor_in_t * +lexbor_in_node_in_noi(const lexbor_in_node_t *node) +{ + return lexbor_in_node_in(node); +} + +bool +lexbor_in_segment_noi(const lexbor_in_node_t *node, const lxb_char_t *data) +{ + return lexbor_in_segment(node, data); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.h new file mode 100644 index 0000000..3e944f2 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/in.h @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_IN_H +#define LEXBOR_IN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" +#include "lexbor/core/dobject.h" + + +typedef struct lexbor_in_node lexbor_in_node_t; +typedef int lexbor_in_opt_t; + +enum lexbor_in_opt { + LEXBOR_IN_OPT_UNDEF = 0x00, + LEXBOR_IN_OPT_READONLY = 0x01, + LEXBOR_IN_OPT_DONE = 0x02, + LEXBOR_IN_OPT_FAKE = 0x04, + LEXBOR_IN_OPT_ALLOC = 0x08 +}; + +typedef struct { + lexbor_dobject_t *nodes; +} +lexbor_in_t; + +struct lexbor_in_node { + size_t offset; + lexbor_in_opt_t opt; + + const lxb_char_t *begin; + const lxb_char_t *end; + const lxb_char_t *use; + + lexbor_in_node_t *next; + lexbor_in_node_t *prev; + + lexbor_in_t *incoming; +}; + + +LXB_API lexbor_in_t * +lexbor_in_create(void); + +LXB_API lxb_status_t +lexbor_in_init(lexbor_in_t *incoming, size_t chunk_size); + +LXB_API void +lexbor_in_clean(lexbor_in_t *incoming); + +LXB_API lexbor_in_t * +lexbor_in_destroy(lexbor_in_t *incoming, bool self_destroy); + + +LXB_API lexbor_in_node_t * +lexbor_in_node_make(lexbor_in_t *incoming, lexbor_in_node_t *last_node, + const lxb_char_t *buf, size_t buf_size); + +LXB_API void +lexbor_in_node_clean(lexbor_in_node_t *node); + +LXB_API lexbor_in_node_t * +lexbor_in_node_destroy(lexbor_in_t *incoming, + lexbor_in_node_t *node, bool self_destroy); + + +LXB_API lexbor_in_node_t * +lexbor_in_node_split(lexbor_in_node_t *node, const lxb_char_t *pos); + +LXB_API lexbor_in_node_t * +lexbor_in_node_find(lexbor_in_node_t *node, const lxb_char_t *pos); + +/** + * Get position by `offset`. + * If position outside of nodes return `begin` position of first node + * in nodes chain. + */ +LXB_API const lxb_char_t * +lexbor_in_node_pos_up(lexbor_in_node_t *node, lexbor_in_node_t **return_node, + const lxb_char_t *pos, size_t offset); + +/** + * Get position by `offset`. + * If position outside of nodes return `end` + * position of last node in nodes chain. + */ +LXB_API const lxb_char_t * +lexbor_in_node_pos_down(lexbor_in_node_t *node, lexbor_in_node_t **return_node, + const lxb_char_t *pos, size_t offset); + +/* + * Inline functions + */ +lxb_inline const lxb_char_t * +lexbor_in_node_begin(const lexbor_in_node_t *node) +{ + return node->begin; +} + +lxb_inline const lxb_char_t * +lexbor_in_node_end(const lexbor_in_node_t *node) +{ + return node->end; +} + +lxb_inline size_t +lexbor_in_node_offset(const lexbor_in_node_t *node) +{ + return node->offset; +} + +lxb_inline lexbor_in_node_t * +lexbor_in_node_next(const lexbor_in_node_t *node) +{ + return node->next; +} + +lxb_inline lexbor_in_node_t * +lexbor_in_node_prev(const lexbor_in_node_t *node) +{ + return node->prev; +} + +lxb_inline lexbor_in_t * +lexbor_in_node_in(const lexbor_in_node_t *node) +{ + return node->incoming; +} + +lxb_inline bool +lexbor_in_segment(const lexbor_in_node_t *node, const lxb_char_t *data) +{ + return node->begin <= data && node->end >= data; +} + +/* + * No inline functions for ABI. + */ +LXB_API const lxb_char_t * +lexbor_in_node_begin_noi(const lexbor_in_node_t *node); + +LXB_API const lxb_char_t * +lexbor_in_node_end_noi(const lexbor_in_node_t *node); + +LXB_API size_t +lexbor_in_node_offset_noi(const lexbor_in_node_t *node); + +LXB_API lexbor_in_node_t * +lexbor_in_node_next_noi(const lexbor_in_node_t *node); + +LXB_API lexbor_in_node_t * +lexbor_in_node_prev_noi(const lexbor_in_node_t *node); + +LXB_API lexbor_in_t * +lexbor_in_node_in_noi(const lexbor_in_node_t *node); + +LXB_API bool +lexbor_in_segment_noi(const lexbor_in_node_t *node, const lxb_char_t *data); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_IN_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/lexbor.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/lexbor.h new file mode 100644 index 0000000..29c4035 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/lexbor.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_H +#define LEXBOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/def.h" + +typedef void *(*lexbor_memory_malloc_f)(size_t size); +typedef void *(*lexbor_memory_realloc_f)(void *dst, size_t size); +typedef void *(*lexbor_memory_calloc_f)(size_t num, size_t size); +typedef void (*lexbor_memory_free_f)(void *dst); + +LXB_API void * +lexbor_malloc(size_t size); + +LXB_API void * +lexbor_realloc(void *dst, size_t size); + +LXB_API void * +lexbor_calloc(size_t num, size_t size); + +LXB_API void * +lexbor_free(void *dst); + +LXB_API lxb_status_t +lexbor_memory_setup(lexbor_memory_malloc_f new_malloc, lexbor_memory_realloc_f new_realloc, + lexbor_memory_calloc_f new_calloc, lexbor_memory_free_f new_free); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.c new file mode 100644 index 0000000..9192af4 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/mem.h" + + +lexbor_mem_t * +lexbor_mem_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_mem_t)); +} + +lxb_status_t +lexbor_mem_init(lexbor_mem_t *mem, size_t min_chunk_size) +{ + if (mem == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (min_chunk_size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + mem->chunk_min_size = lexbor_mem_align(min_chunk_size); + + /* Create first chunk */ + mem->chunk = lexbor_mem_chunk_make(mem, mem->chunk_min_size); + if (mem->chunk == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + mem->chunk_length = 1; + mem->chunk_first = mem->chunk; + + return LXB_STATUS_OK; +} + +void +lexbor_mem_clean(lexbor_mem_t *mem) +{ + lexbor_mem_chunk_t *prev, *chunk; + + if (mem == NULL) { + return; + } + + chunk = mem->chunk; + + while (chunk->prev) { + prev = chunk->prev; + + chunk->data = lexbor_free(chunk->data); + lexbor_free(chunk); + + chunk = prev; + } + + chunk->next = NULL; + chunk->length = 0; + + mem->chunk = mem->chunk_first; + mem->chunk_length = 1; +} + +lexbor_mem_t * +lexbor_mem_destroy(lexbor_mem_t *mem, bool destroy_self) +{ + lexbor_mem_chunk_t *chunk, *prev; + + if (mem == NULL) { + return NULL; + } + + /* Destroy all chunk */ + if (mem->chunk) { + chunk = mem->chunk; + + while (chunk) { + prev = chunk->prev; + lexbor_mem_chunk_destroy(mem, chunk, true); + chunk = prev; + } + + mem->chunk = NULL; + } + + if (destroy_self) { + return lexbor_free(mem); + } + + return mem; +} + +uint8_t * +lexbor_mem_chunk_init(lexbor_mem_t *mem, + lexbor_mem_chunk_t *chunk, size_t length) +{ + length = lexbor_mem_align(length); + + if (length > mem->chunk_min_size) { + if (mem->chunk_min_size > (SIZE_MAX - length)) { + chunk->size = length; + } + else { + chunk->size = length + mem->chunk_min_size; + } + } + else { + chunk->size = mem->chunk_min_size; + } + + chunk->length = 0; + chunk->data = lexbor_malloc(chunk->size * sizeof(uint8_t)); + + return chunk->data; +} + +lexbor_mem_chunk_t * +lexbor_mem_chunk_make(lexbor_mem_t *mem, size_t length) +{ + lexbor_mem_chunk_t *chunk = lexbor_calloc(1, sizeof(lexbor_mem_chunk_t)); + + if (chunk == NULL) { + return NULL; + } + + if (lexbor_mem_chunk_init(mem, chunk, length) == NULL) { + return lexbor_free(chunk); + } + + return chunk; +} + +lexbor_mem_chunk_t * +lexbor_mem_chunk_destroy(lexbor_mem_t *mem, + lexbor_mem_chunk_t *chunk, bool self_destroy) +{ + if (chunk == NULL || mem == NULL) { + return NULL; + } + + if (chunk->data) { + chunk->data = lexbor_free(chunk->data); + } + + if (self_destroy) { + return lexbor_free(chunk); + } + + return chunk; +} + +void * +lexbor_mem_alloc(lexbor_mem_t *mem, size_t length) +{ + if (length == 0) { + return NULL; + } + + length = lexbor_mem_align(length); + + if ((mem->chunk->length + length) > mem->chunk->size) { + if ((SIZE_MAX - mem->chunk_length) == 0) { + return NULL; + } + + mem->chunk->next = lexbor_mem_chunk_make(mem, length); + if (mem->chunk->next == NULL) { + return NULL; + } + + mem->chunk->next->prev = mem->chunk; + mem->chunk = mem->chunk->next; + + mem->chunk_length++; + } + + mem->chunk->length += length; + + return &mem->chunk->data[(mem->chunk->length - length)]; +} + +void * +lexbor_mem_calloc(lexbor_mem_t *mem, size_t length) +{ + void *data = lexbor_mem_alloc(mem, length); + + if (data != NULL) { + memset(data, 0, length); + } + + return data; +} + +/* + * No inline functions for ABI. + */ +size_t +lexbor_mem_current_length_noi(lexbor_mem_t *mem) +{ + return lexbor_mem_current_length(mem); +} + +size_t +lexbor_mem_current_size_noi(lexbor_mem_t *mem) +{ + return lexbor_mem_current_size(mem); +} + +size_t +lexbor_mem_chunk_length_noi(lexbor_mem_t *mem) +{ + return lexbor_mem_chunk_length(mem); +} +size_t +lexbor_mem_align_noi(size_t size) +{ + return lexbor_mem_align(size); +} + +size_t +lexbor_mem_align_floor_noi(size_t size) +{ + return lexbor_mem_align_floor(size); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.h new file mode 100644 index 0000000..f245528 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mem.h @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_MEM_H +#define LEXBOR_MEM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" + + +typedef struct lexbor_mem_chunk lexbor_mem_chunk_t; +typedef struct lexbor_mem lexbor_mem_t; + +struct lexbor_mem_chunk { + uint8_t *data; + size_t length; + size_t size; + + lexbor_mem_chunk_t *next; + lexbor_mem_chunk_t *prev; +}; + +struct lexbor_mem { + lexbor_mem_chunk_t *chunk; + lexbor_mem_chunk_t *chunk_first; + + size_t chunk_min_size; + size_t chunk_length; +}; + + +LXB_API lexbor_mem_t * +lexbor_mem_create(void); + +LXB_API lxb_status_t +lexbor_mem_init(lexbor_mem_t *mem, size_t min_chunk_size); + +LXB_API void +lexbor_mem_clean(lexbor_mem_t *mem); + +LXB_API lexbor_mem_t * +lexbor_mem_destroy(lexbor_mem_t *mem, bool destroy_self); + + +/* + * The memory allocated in lexbor_mem_chunk_* functions needs to be freed + * by lexbor_mem_chunk_destroy function. + * + * This memory will not be automatically freed by a function lexbor_mem_destroy. + */ +LXB_API uint8_t * +lexbor_mem_chunk_init(lexbor_mem_t *mem, + lexbor_mem_chunk_t *chunk, size_t length); + +LXB_API lexbor_mem_chunk_t * +lexbor_mem_chunk_make(lexbor_mem_t *mem, size_t length); + +LXB_API lexbor_mem_chunk_t * +lexbor_mem_chunk_destroy(lexbor_mem_t *mem, + lexbor_mem_chunk_t *chunk, bool self_destroy); + +/* + * The memory allocated in lexbor_mem_alloc and lexbor_mem_calloc function + * will be freeds after calling lexbor_mem_destroy function. + */ +LXB_API void * +lexbor_mem_alloc(lexbor_mem_t *mem, size_t length); + +LXB_API void * +lexbor_mem_calloc(lexbor_mem_t *mem, size_t length); + + +/* + * Inline functions + */ +lxb_inline size_t +lexbor_mem_current_length(lexbor_mem_t *mem) +{ + return mem->chunk->length; +} + +lxb_inline size_t +lexbor_mem_current_size(lexbor_mem_t *mem) +{ + return mem->chunk->size; +} + +lxb_inline size_t +lexbor_mem_chunk_length(lexbor_mem_t *mem) +{ + return mem->chunk_length; +} + +lxb_inline size_t +lexbor_mem_align(size_t size) +{ + return ((size % LEXBOR_MEM_ALIGN_STEP) != 0) + ? size + (LEXBOR_MEM_ALIGN_STEP - (size % LEXBOR_MEM_ALIGN_STEP)) + : size; +} + +lxb_inline size_t +lexbor_mem_align_floor(size_t size) +{ + return ((size % LEXBOR_MEM_ALIGN_STEP) != 0) + ? size - (size % LEXBOR_MEM_ALIGN_STEP) + : size; +} + +/* + * No inline functions for ABI. + */ +LXB_API size_t +lexbor_mem_current_length_noi(lexbor_mem_t *mem); + +LXB_API size_t +lexbor_mem_current_size_noi(lexbor_mem_t *mem); + +LXB_API size_t +lexbor_mem_chunk_length_noi(lexbor_mem_t *mem); + +LXB_API size_t +lexbor_mem_align_noi(size_t size); + +LXB_API size_t +lexbor_mem_align_floor_noi(size_t size); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_MEM_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.c new file mode 100644 index 0000000..2e629fb --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.c @@ -0,0 +1,421 @@ +/* + * Copyright (C) 2018-2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/mraw.h" + + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + #include +#endif + + +#define lexbor_mraw_meta_set(data, size) \ + do { \ + memcpy(data, size, sizeof(size_t)); \ + } \ + while (0) + +#define lexbor_mraw_data_begin(data) \ + &((uint8_t *) (data))[ lexbor_mraw_meta_size() ] + + +lxb_inline void * +lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin, + size_t size, size_t begin_len, size_t new_size, + bool *is_valid); + + +lexbor_mraw_t * +lexbor_mraw_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_mraw_t)); +} + +lxb_status_t +lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size) +{ + lxb_status_t status; + + if (mraw == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (chunk_size == 0) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + /* Init memory */ + mraw->mem = lexbor_mem_create(); + + status = lexbor_mem_init(mraw->mem, chunk_size + lexbor_mraw_meta_size()); + if (status) { + return status; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(mraw->mem->chunk->data, mraw->mem->chunk->size); +#endif + + /* Cache */ + mraw->cache = lexbor_bst_create(); + + status = lexbor_bst_init(mraw->cache, 512); + if (status) { + return status; + } + + mraw->ref_count = 0; + + return LXB_STATUS_OK; +} + +void +lexbor_mraw_clean(lexbor_mraw_t *mraw) +{ + if (mraw != NULL) { + lexbor_mem_clean(mraw->mem); + lexbor_bst_clean(mraw->cache); + + mraw->ref_count = 0; + } +} + +lexbor_mraw_t * +lexbor_mraw_destroy(lexbor_mraw_t *mraw, bool destroy_self) +{ + if (mraw == NULL) { + return NULL; + } + + mraw->mem = lexbor_mem_destroy(mraw->mem, true); + mraw->cache = lexbor_bst_destroy(mraw->cache, true); + + if (destroy_self) { + return lexbor_free(mraw); + } + + return mraw; +} + +lxb_inline void * +lexbor_mraw_mem_alloc(lexbor_mraw_t *mraw, size_t length) +{ + uint8_t *data; + lexbor_mem_t *mem = mraw->mem; + + if (length == 0) { + return NULL; + } + + if ((mem->chunk->length + length) > mem->chunk->size) { + lexbor_mem_chunk_t *chunk = mem->chunk; + + if ((SIZE_MAX - mem->chunk_length) == 0) { + return NULL; + } + + if (chunk->length == 0) { + lexbor_mem_chunk_destroy(mem, chunk, false); + lexbor_mem_chunk_init(mem, chunk, length); + + chunk->length = length; + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(chunk->data, chunk->size); +#endif + + return chunk->data; + } + + size_t diff = lexbor_mem_align_floor(chunk->size - chunk->length); + + /* Save tail to cache */ + if (diff > lexbor_mraw_meta_size()) { + diff -= lexbor_mraw_meta_size(); + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_UNPOISON_MEMORY_REGION(&chunk->data[chunk->length], + lexbor_mraw_meta_size()); +#endif + + lexbor_mraw_meta_set(&chunk->data[chunk->length], &diff); + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(&chunk->data[chunk->length], + diff + lexbor_mraw_meta_size()); +#endif + + lexbor_bst_insert(mraw->cache, + lexbor_bst_root_ref(mraw->cache), diff, + lexbor_mraw_data_begin(&chunk->data[chunk->length])); + + chunk->length = chunk->size; + } + + chunk->next = lexbor_mem_chunk_make(mem, length); + if (chunk->next == NULL) { + return NULL; + } + + chunk->next->prev = chunk; + mem->chunk = chunk->next; + + mem->chunk_length++; + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(mem->chunk->data, mem->chunk->size); +#endif + } + + data = &mem->chunk->data[ mem->chunk->length ]; + mem->chunk->length += length; + + return data; +} + +void * +lexbor_mraw_alloc(lexbor_mraw_t *mraw, size_t size) +{ + void *data; + + size = lexbor_mem_align(size); + + if (mraw->cache->tree_length != 0) { + data = lexbor_bst_remove_close(mraw->cache, + lexbor_bst_root_ref(mraw->cache), + size, NULL); + if (data != NULL) { + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size(); + + /* Set unpoison for current data size */ + ASAN_UNPOISON_MEMORY_REGION(real_data, lexbor_mraw_meta_size()); + + size_t cur_size = lexbor_mraw_data_size(data); + + ASAN_UNPOISON_MEMORY_REGION(real_data, + (cur_size + lexbor_mraw_meta_size())); +#endif + + mraw->ref_count++; + + return data; + } + } + + data = lexbor_mraw_mem_alloc(mraw, (size + lexbor_mraw_meta_size())); + + if (data == NULL) { + return NULL; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_UNPOISON_MEMORY_REGION(data, (size + lexbor_mraw_meta_size())); +#endif + + mraw->ref_count++; + + lexbor_mraw_meta_set(data, &size); + return lexbor_mraw_data_begin(data); +} + +void * +lexbor_mraw_calloc(lexbor_mraw_t *mraw, size_t size) +{ + void *data = lexbor_mraw_alloc(mraw, size); + + if (data != NULL) { + memset(data, 0, lexbor_mraw_data_size(data)); + } + + return data; +} + +/* + * TODO: I don't really like this interface. Perhaps need to simplify. + */ +lxb_inline void * +lexbor_mraw_realloc_tail(lexbor_mraw_t *mraw, void *data, void *begin, + size_t size, size_t begin_len, size_t new_size, + bool *is_valid) +{ + size_t length; + uint8_t *tmp; + lexbor_mem_chunk_t *chunk = mraw->mem->chunk; + + if (chunk->size > (begin_len + new_size)) { + *is_valid = true; + + if (new_size == 0) { + chunk->length = begin_len - lexbor_mraw_meta_size(); + return NULL; + } + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_UNPOISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size()); +#endif + + chunk->length = begin_len + new_size; + memcpy(begin, &new_size, sizeof(size_t)); + + return data; + } + + /* + * If the tail is short then we increase the current data. + */ + if (begin_len == lexbor_mraw_meta_size()) { +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_UNPOISON_MEMORY_REGION(chunk->data, chunk->size); +#endif + *is_valid = true; + + length = lexbor_mem_align(new_size + lexbor_mraw_meta_size() + + mraw->mem->chunk_min_size); + + tmp = lexbor_realloc(chunk->data, length); + if (tmp == NULL) { + return NULL; + } + + chunk->data = tmp; + chunk->size = length; + chunk->length = new_size + lexbor_mraw_meta_size(); + + lexbor_mraw_meta_set(tmp, &new_size); + + return lexbor_mraw_data_begin(tmp); + } + + *is_valid = false; + + /* + * Next, this piece will go into the cache. + */ + size = lexbor_mem_align_floor(size + (chunk->size - chunk->length)); + memcpy(begin, &size, sizeof(size_t)); + + chunk->length = chunk->size; + + return NULL; +} + +void * +lexbor_mraw_realloc(lexbor_mraw_t *mraw, void *data, size_t new_size) +{ + void *begin; + size_t size, begin_len; + lexbor_mem_chunk_t *chunk = mraw->mem->chunk; + + begin = ((uint8_t *) data) - lexbor_mraw_meta_size(); + memcpy(&size, begin, sizeof(size_t)); + + new_size = lexbor_mem_align(new_size); + + /* + * Look, whether there is an opportunity + * to prolong the current data in chunk? + */ + if (chunk->length >= size) { + begin_len = chunk->length - size; + + if (&chunk->data[begin_len] == data) { + bool is_valid; + void *ptr = lexbor_mraw_realloc_tail(mraw, data, begin, + size, begin_len, new_size, + &is_valid); + if (is_valid == true) { + return ptr; + } + } + } + + if (new_size < size) { + if (new_size == 0) { + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(begin, size + lexbor_mraw_meta_size()); +#endif + mraw->ref_count--; + + lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache), + size, data); + return NULL; + } + + size_t diff = lexbor_mem_align_floor(size - new_size); + + if (diff > lexbor_mraw_meta_size()) { + memcpy(begin, &new_size, sizeof(size_t)); + + begin = &((uint8_t *) data)[new_size]; + new_size = diff - lexbor_mraw_meta_size(); + + lexbor_mraw_meta_set(begin, &new_size); + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + ASAN_POISON_MEMORY_REGION(begin, new_size + lexbor_mraw_meta_size()); +#endif + lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache), + new_size, lexbor_mraw_data_begin(begin)); + } + + return data; + } + + begin = lexbor_mraw_alloc(mraw, new_size); + if (begin == NULL) { + return NULL; + } + + if (size != 0) { + memcpy(begin, data, sizeof(uint8_t) * size); + } + + lexbor_mraw_free(mraw, data); + + return begin; +} + +void * +lexbor_mraw_free(lexbor_mraw_t *mraw, void *data) +{ + size_t size = lexbor_mraw_data_size(data); + +#if defined(LEXBOR_HAVE_ADDRESS_SANITIZER) + uint8_t *real_data = ((uint8_t *) data) - lexbor_mraw_meta_size(); + ASAN_POISON_MEMORY_REGION(real_data, size + lexbor_mraw_meta_size()); +#endif + + lexbor_bst_insert(mraw->cache, lexbor_bst_root_ref(mraw->cache), + size, data); + + mraw->ref_count--; + + return NULL; +} + +/* + * No inline functions for ABI. + */ +size_t +lexbor_mraw_data_size_noi(void *data) +{ + return lexbor_mraw_data_size(data); +} + +void +lexbor_mraw_data_size_set_noi(void *data, size_t size) +{ + lexbor_mraw_data_size_set(data, size); +} + +void * +lexbor_mraw_dup_noi(lexbor_mraw_t *mraw, const void *src, size_t size) +{ + return lexbor_mraw_dup(mraw, src, size); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.h new file mode 100644 index 0000000..1ffb64d --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/mraw.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_MRAW_H +#define LEXBOR_MRAW_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" +#include "lexbor/core/mem.h" +#include "lexbor/core/bst.h" + + +#define lexbor_mraw_meta_size() \ + (((sizeof(size_t) % LEXBOR_MEM_ALIGN_STEP) != 0) \ + ? sizeof(size_t) \ + + (LEXBOR_MEM_ALIGN_STEP - (sizeof(size_t) % LEXBOR_MEM_ALIGN_STEP)) \ + : sizeof(size_t)) + + +typedef struct { + lexbor_mem_t *mem; + lexbor_bst_t *cache; + size_t ref_count; +} +lexbor_mraw_t; + + +LXB_API lexbor_mraw_t * +lexbor_mraw_create(void); + +LXB_API lxb_status_t +lexbor_mraw_init(lexbor_mraw_t *mraw, size_t chunk_size); + +LXB_API void +lexbor_mraw_clean(lexbor_mraw_t *mraw); + +LXB_API lexbor_mraw_t * +lexbor_mraw_destroy(lexbor_mraw_t *mraw, bool destroy_self); + + +LXB_API void * +lexbor_mraw_alloc(lexbor_mraw_t *mraw, size_t size); + +LXB_API void * +lexbor_mraw_calloc(lexbor_mraw_t *mraw, size_t size); + +LXB_API void * +lexbor_mraw_realloc(lexbor_mraw_t *mraw, void *data, size_t new_size); + +LXB_API void * +lexbor_mraw_free(lexbor_mraw_t *mraw, void *data); + + +/* + * Inline functions + */ +lxb_inline size_t +lexbor_mraw_data_size(void *data) +{ + return *((size_t *) (((uint8_t *) data) - lexbor_mraw_meta_size())); +} + +lxb_inline void +lexbor_mraw_data_size_set(void *data, size_t size) +{ + data = (((uint8_t *) data) - lexbor_mraw_meta_size()); + memcpy(data, &size, sizeof(size_t)); +} + +lxb_inline void * +lexbor_mraw_dup(lexbor_mraw_t *mraw, const void *src, size_t size) +{ + void *data = lexbor_mraw_alloc(mraw, size); + + if (data != NULL) { + memcpy(data, src, size); + } + + return data; +} + +lxb_inline size_t +lexbor_mraw_reference_count(lexbor_mraw_t *mraw) +{ + return mraw->ref_count; +} + + +/* + * No inline functions for ABI. + */ +LXB_API size_t +lexbor_mraw_data_size_noi(void *data); + +LXB_API void +lexbor_mraw_data_size_set_noi(void *data, size_t size); + +LXB_API void * +lexbor_mraw_dup_noi(lexbor_mraw_t *mraw, const void *src, size_t size); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_MRAW_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/perf.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/perf.h new file mode 100644 index 0000000..49bf0bb --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/perf.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2018-2024 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_PERF_H +#define LEXBOR_PERF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +LXB_API void * +lexbor_perf_create(void); + +LXB_API void +lexbor_perf_clean(void *perf); + +LXB_API void +lexbor_perf_destroy(void *perf); + +LXB_API lxb_status_t +lexbor_perf_begin(void *perf); + +LXB_API lxb_status_t +lexbor_perf_end(void *perf); + +LXB_API double +lexbor_perf_in_sec(void *perf); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_PERF_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.c new file mode 100644 index 0000000..71344b0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/plog.h" + + +lxb_status_t +lexbor_plog_init(lexbor_plog_t *plog, size_t init_size, size_t struct_size) +{ + lxb_status_t status; + + if (plog == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + if (struct_size < sizeof(lexbor_plog_entry_t)) { + struct_size = sizeof(lexbor_plog_entry_t); + } + + status = lexbor_array_obj_init(&plog->list, init_size, struct_size); + if (status != LXB_STATUS_OK) { + return status; + } + + return LXB_STATUS_OK; +} + +lexbor_plog_t * +lexbor_plog_destroy(lexbor_plog_t *plog, bool self_destroy) +{ + if (plog == NULL) { + return NULL; + } + + lexbor_array_obj_destroy(&plog->list, false); + + if (self_destroy) { + return lexbor_free(plog); + } + + return plog; +} + +/* + * No inline functions. + */ +lexbor_plog_t * +lexbor_plog_create_noi(void) +{ + return lexbor_plog_create(); +} + +void +lexbor_plog_clean_noi(lexbor_plog_t *plog) +{ + lexbor_plog_clean(plog); +} + +void * +lexbor_plog_push_noi(lexbor_plog_t *plog, const lxb_char_t *data, void *ctx, + unsigned id) +{ + return lexbor_plog_push(plog, data, ctx, id); +} + +size_t +lexbor_plog_length_noi(lexbor_plog_t *plog) +{ + return lexbor_plog_length(plog); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.h new file mode 100644 index 0000000..91b35bf --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/plog.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_PLOG_H +#define LEXBOR_PLOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/array_obj.h" + + +typedef struct { + const lxb_char_t *data; + void *context; + unsigned id; +} +lexbor_plog_entry_t; + +typedef struct { + lexbor_array_obj_t list; +} +lexbor_plog_t; + + +LXB_API lxb_status_t +lexbor_plog_init(lexbor_plog_t *plog, size_t init_size, size_t struct_size); + +LXB_API lexbor_plog_t * +lexbor_plog_destroy(lexbor_plog_t *plog, bool self_destroy); + + +/* + * Inline functions + */ +lxb_inline lexbor_plog_t * +lexbor_plog_create(void) +{ + return (lexbor_plog_t *) lexbor_calloc(1, sizeof(lexbor_plog_t)); +} + +lxb_inline void +lexbor_plog_clean(lexbor_plog_t *plog) +{ + lexbor_array_obj_clean(&plog->list); +} + +lxb_inline void * +lexbor_plog_push(lexbor_plog_t *plog, const lxb_char_t *data, void *ctx, + unsigned id) +{ + lexbor_plog_entry_t *entry; + + if (plog == NULL) { + return NULL; + } + + entry = (lexbor_plog_entry_t *) lexbor_array_obj_push(&plog->list); + if (entry == NULL) { + return NULL; + } + + entry->data = data; + entry->context = ctx; + entry->id = id; + + return (void *) entry; +} + +lxb_inline size_t +lexbor_plog_length(lexbor_plog_t *plog) +{ + return lexbor_array_obj_length(&plog->list); +} + +/* + * No inline functions for ABI. + */ +LXB_API lexbor_plog_t * +lexbor_plog_create_noi(void); + +LXB_API void +lexbor_plog_clean_noi(lexbor_plog_t *plog); + +LXB_API void * +lexbor_plog_push_noi(lexbor_plog_t *plog, const lxb_char_t *data, void *ctx, + unsigned id); + +LXB_API size_t +lexbor_plog_length_noi(lexbor_plog_t *plog); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_PLOG_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.c new file mode 100644 index 0000000..63a6f20 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/str.h" +#include "lexbor/core/print.h" + + +#define lexbor_sprintf_append(dst, end, src, length) \ + do { \ + if ((size_t) ((end) - (dst)) < (length)) { \ + return (end) - (dst); \ + } \ + \ + memcpy((dst), (src), (length)); \ + (dst) += (length); \ + } \ + while (false) + + +size_t +lexbor_printf_size(const char *format, ...) +{ + size_t ret; + va_list va; + + va_start(va, format); + ret = lexbor_vprintf_size(format, va); + va_end(va); + + return ret; +} + +size_t +lexbor_vprintf_size(const char *format, va_list va) +{ + char c; + const char *begin, *cdata; + size_t size; + lexbor_str_t *str; + + begin = format; + size = 0; + + while (true) { + c = *format; + + if (c == '%') { + c = format[1]; + + switch (c) { + case '\0': + return size + (format - begin) + 1; + + case 's': + cdata = va_arg(va, const char *); + size += (format - begin) + strlen(cdata); + break; + + case 'S': + str = va_arg(va, lexbor_str_t *); + size += (format - begin) + str->length; + break; + + case '%': + size += (format - begin) + 1; + break; + + default: + return LXB_PRINT_ERROR; + } + + format++; + begin = format + 1; + } + else if (c == '\0') { + return size + (format - begin); + } + + format++; + } +} + +size_t +lexbor_sprintf(lxb_char_t *dst, size_t size, const char *format, ...) +{ + size_t ret; + va_list va; + + va_start(va, format); + ret = lexbor_vsprintf(dst, size, format, va); + va_end(va); + + return ret; +} + +size_t +lexbor_vsprintf(lxb_char_t *dst, size_t size, const char *format, va_list va) +{ + char c; + const char *begin, *cdata; + lxb_char_t *end, *start; + lexbor_str_t *str; + + begin = format; + start = dst; + end = dst + size; + + while (true) { + c = *format; + + if (c == '%') { + c = format[1]; + + switch (c) { + case '\0': + size = (format - begin) + 1; + lexbor_sprintf_append(dst, end, begin, size); + goto done; + + case 's': + size = format - begin; + lexbor_sprintf_append(dst, end, begin, size); + + cdata = va_arg(va, const char *); + size = strlen(cdata); + lexbor_sprintf_append(dst, end, cdata, size); + break; + + case 'S': + size = format - begin; + lexbor_sprintf_append(dst, end, begin, size); + + str = va_arg(va, lexbor_str_t *); + lexbor_sprintf_append(dst, end, str->data, str->length); + break; + + case '%': + size = (format - begin) + 1; + lexbor_sprintf_append(dst, end, begin, size); + break; + + default: + return LXB_PRINT_ERROR; + } + + format++; + begin = format + 1; + } + else if (c == '\0') { + size = format - begin; + lexbor_sprintf_append(dst, end, begin, size); + goto done; + } + + format++; + } + +done: + + if (end - dst > 0) { + *dst = '\0'; + } + + return dst - start; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.h new file mode 100644 index 0000000..5e62245 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/print.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_PRINT_H +#define LEXBOR_PRINT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" + + +#define LXB_PRINT_ERROR ((size_t) - 1) + + +LXB_API size_t +lexbor_printf_size(const char *format, ...); + +LXB_API size_t +lexbor_vprintf_size(const char *format, va_list va); + +LXB_API size_t +lexbor_sprintf(lxb_char_t *dst, size_t size, const char *format, ...); + +LXB_API size_t +lexbor_vsprintf(lxb_char_t *dst, size_t size, const char *format, va_list va); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_PRINT_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/sbst.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/sbst.h new file mode 100644 index 0000000..b0fbc54 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/sbst.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_SBST_H +#define LEXBOR_SBST_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" + + +typedef struct { + lxb_char_t key; + + void *value; + size_t value_len; + + size_t left; + size_t right; + size_t next; +} +lexbor_sbst_entry_static_t; + + +/* + * Inline functions + */ +lxb_inline const lexbor_sbst_entry_static_t * +lexbor_sbst_entry_static_find(const lexbor_sbst_entry_static_t *strt, + const lexbor_sbst_entry_static_t *root, + const lxb_char_t key) +{ + while (root != strt) { + if (root->key == key) { + return root; + } + else if (key > root->key) { + root = &strt[root->right]; + } + else { + root = &strt[root->left]; + } + } + + return NULL; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_SBST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.c new file mode 100644 index 0000000..599e3c7 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.c @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/serialize.h" +#include "lexbor/core/str.h" + + +lxb_status_t +lexbor_serialize_length_cb(const lxb_char_t *data, size_t length, void *ctx) +{ + *((size_t *) ctx) += length; + return LXB_STATUS_OK; +} + +lxb_status_t +lexbor_serialize_copy_cb(const lxb_char_t *data, size_t length, void *ctx) +{ + lexbor_str_t *str = ctx; + + memcpy(str->data + str->length, data, length); + str->length += length; + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.h new file mode 100644 index 0000000..d6352f2 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/serialize.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_SERIALIZE_H +#define LEXBOR_SERIALIZE_H + + +#include "lexbor/core/base.h" + + +#define lexbor_serialize_write(cb, data, length, ctx, status) \ + do { \ + (status) = (cb)((lxb_char_t *) (data), (length), (ctx)); \ + if ((status) != LXB_STATUS_OK) { \ + return (status); \ + } \ + } \ + while (false) + + +LXB_API lxb_status_t +lexbor_serialize_length_cb(const lxb_char_t *data, size_t length, void *ctx); + + +LXB_API lxb_status_t +lexbor_serialize_copy_cb(const lxb_char_t *data, size_t length, void *ctx); + + +#endif /* LEXBOR_SERIALIZE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.c new file mode 100644 index 0000000..c275345 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/shs.h" +#include "lexbor/core/str.h" + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_lowercase[256]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_uppercase[256]; +#endif + +#define lexbor_shs_make_id_m(key, size, table_size) \ + (((((key[0] * key[size - 1]) * key[0]) + size) % table_size) + 0x01) + +#define lexbor_shs_make_id_lower_m(key, size, table_size) \ + (((((lexbor_str_res_map_lowercase[key[0]] \ + * lexbor_str_res_map_lowercase[key[size - 1]]) \ + * lexbor_str_res_map_lowercase[key[0]]) \ + + size) \ + % table_size) + 0x01) + +#define lexbor_shs_make_id_upper_m(key, size, table_size) \ + (((((lexbor_str_res_map_uppercase[key[0]] \ + * lexbor_str_res_map_uppercase[key[size - 1]]) \ + * lexbor_str_res_map_uppercase[key[0]]) \ + + size) \ + % table_size) + 0x01) + + +const lexbor_shs_entry_t * +lexbor_shs_entry_get_static(const lexbor_shs_entry_t *root, + const lxb_char_t *key, size_t key_len) +{ + const lexbor_shs_entry_t *entry; + entry = root + lexbor_shs_make_id_m(key, key_len, root->key_len); + + while (entry->key != NULL) + { + if (entry->key_len == key_len) { + if (lexbor_str_data_ncmp((const lxb_char_t *) entry->key, + key, key_len)) + { + return entry; + } + + entry = &root[entry->next]; + } + else if (entry->key_len > key_len) { + return NULL; + } + else { + entry = &root[entry->next]; + } + } + + return NULL; +} + +const lexbor_shs_entry_t * +lexbor_shs_entry_get_lower_static(const lexbor_shs_entry_t *root, + const lxb_char_t *key, size_t key_len) +{ + const lexbor_shs_entry_t *entry; + entry = root + lexbor_shs_make_id_lower_m(key, key_len, root->key_len); + + while (entry->key != NULL) + { + if (entry->key_len == key_len) { + if (lexbor_str_data_nlocmp_right((const lxb_char_t *) entry->key, + key, key_len)) + { + return entry; + } + + entry = &root[entry->next]; + } + else if (entry->key_len > key_len) { + return NULL; + } + else { + entry = &root[entry->next]; + } + } + + return NULL; +} + +const lexbor_shs_entry_t * +lexbor_shs_entry_get_upper_static(const lexbor_shs_entry_t *root, + const lxb_char_t *key, size_t key_len) +{ + const lexbor_shs_entry_t *entry; + entry = root + lexbor_shs_make_id_upper_m(key, key_len, root->key_len); + + while (entry->key != NULL) + { + if (entry->key_len == key_len) { + if (lexbor_str_data_nupcmp_right((const lxb_char_t *) entry->key, + key, key_len)) + { + return entry; + } + + entry = &root[entry->next]; + } + else if (entry->key_len > key_len) { + return NULL; + } + else { + entry = &root[entry->next]; + } + } + + return NULL; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.h new file mode 100644 index 0000000..7a63a07 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/shs.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_SHS_H +#define LEXBOR_SHS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "lexbor/core/base.h" + + +typedef struct { + char *key; + void *value; + + size_t key_len; + size_t next; +} +lexbor_shs_entry_t; + +typedef struct { + uint32_t key; + void *value; + + size_t next; +} +lexbor_shs_hash_t; + + +LXB_API const lexbor_shs_entry_t * +lexbor_shs_entry_get_static(const lexbor_shs_entry_t *tree, + const lxb_char_t *key, size_t size); + +LXB_API const lexbor_shs_entry_t * +lexbor_shs_entry_get_lower_static(const lexbor_shs_entry_t *root, + const lxb_char_t *key, size_t key_len); + +LXB_API const lexbor_shs_entry_t * +lexbor_shs_entry_get_upper_static(const lexbor_shs_entry_t *root, + const lxb_char_t *key, size_t key_len); + +/* + * Inline functions + */ +lxb_inline const lexbor_shs_hash_t * +lexbor_shs_hash_get_static(const lexbor_shs_hash_t *table, + const size_t table_size, const uint32_t key) +{ + const lexbor_shs_hash_t *entry; + + entry = &table[ (key % table_size) + 1 ]; + + do { + if (entry->key == key) { + return entry; + } + + entry = &table[entry->next]; + } + while (entry != table); + + return NULL; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_SHS_H */ + + + + + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.c new file mode 100644 index 0000000..3d164eb --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.c @@ -0,0 +1,667 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/str.h" +#include "lexbor/core/str_res.h" + + +lexbor_str_t * +lexbor_str_create(void) +{ + return lexbor_calloc(1, sizeof(lexbor_str_t)); +} + +lxb_char_t * +lexbor_str_init(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t size) +{ + if (str == NULL) { + return NULL; + } + + str->data = lexbor_mraw_alloc(mraw, (size + 1)); + str->length = 0; + + if (str->data != NULL) { + *str->data = '\0'; + } + + return str->data; +} + +lxb_char_t * +lexbor_str_init_append(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *data, size_t length) +{ + lxb_char_t *p; + + if (str == NULL) { + return NULL; + } + + p = lexbor_mraw_alloc(mraw, (length + 1)); + if (p == NULL) { + return NULL; + } + + memcpy(p, data, length); + + p[length] = '\0'; + + str->data = p; + str->length = length; + + return p; +} + +void +lexbor_str_clean(lexbor_str_t *str) +{ + str->length = 0; +} + +void +lexbor_str_clean_all(lexbor_str_t *str) +{ + memset(str, 0, sizeof(lexbor_str_t)); +} + +lexbor_str_t * +lexbor_str_destroy(lexbor_str_t *str, lexbor_mraw_t *mraw, bool destroy_obj) +{ + if (str == NULL) { + return NULL; + } + + if (str->data != NULL) { + lexbor_str_clean(str); + str->data = lexbor_mraw_free(mraw, str->data); + } + + if (destroy_obj) { + return lexbor_free(str); + } + + return str; +} + +lxb_char_t * +lexbor_str_realloc(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t new_size) +{ + lxb_char_t *tmp = lexbor_mraw_realloc(mraw, str->data, new_size); + if (tmp == NULL) { + return NULL; + } + + str->data = tmp; + + return tmp; +} + +lxb_char_t * +lexbor_str_check_size(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t plus_len) +{ + lxb_char_t *tmp; + + if (str->length > (SIZE_MAX - plus_len)) { + return NULL; + } + + if ((str->length + plus_len) <= lexbor_str_size(str)) { + return str->data; + } + + tmp = lexbor_mraw_realloc(mraw, str->data, (str->length + plus_len)); + if (tmp == NULL) { + return NULL; + } + + str->data = tmp; + + return tmp; +} + +/* Append API */ +lxb_char_t * +lexbor_str_append(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *buff, size_t length) +{ + lxb_char_t *data_begin; + + if (length == 0) { + return str->data; + } + + lexbor_str_check_size_arg_m(str, lexbor_str_size(str), + mraw, (length + 1), NULL); + + data_begin = &str->data[str->length]; + memcpy(data_begin, buff, sizeof(lxb_char_t) * length); + + str->length += length; + str->data[str->length] = '\0'; + + return data_begin; +} + +lxb_char_t * +lexbor_str_append_before(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *buff, size_t length) +{ + lxb_char_t *data_begin; + + lexbor_str_check_size_arg_m(str, lexbor_str_size(str), + mraw, (length + 1), NULL); + + data_begin = &str->data[str->length]; + + memmove(&str->data[length], str->data, sizeof(lxb_char_t) * str->length); + memcpy(str->data, buff, sizeof(lxb_char_t) * length); + + str->length += length; + str->data[str->length] = '\0'; + + return data_begin; +} + +lxb_char_t * +lexbor_str_append_one(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t data) +{ + lexbor_str_check_size_arg_m(str, lexbor_str_size(str), mraw, 2, NULL); + + str->data[str->length] = data; + + str->length += 1; + str->data[str->length] = '\0'; + + return &str->data[(str->length - 1)]; +} + +lxb_char_t * +lexbor_str_append_lowercase(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *data, size_t length) +{ + size_t i; + lxb_char_t *data_begin; + + lexbor_str_check_size_arg_m(str, lexbor_str_size(str), + mraw, (length + 1), NULL); + + data_begin = &str->data[str->length]; + + for (i = 0; i < length; i++) { + data_begin[i] = lexbor_str_res_map_lowercase[ data[i] ]; + } + + data_begin[i] = '\0'; + str->length += length; + + return data_begin; +} + +lxb_char_t * +lexbor_str_append_with_rep_null_chars(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *buff, size_t length) +{ + const lxb_char_t *pos, *res, *end; + size_t current_len = str->length; + + lexbor_str_check_size_arg_m(str, lexbor_str_size(str), + mraw, (length + 1), NULL); + end = buff + length; + + while (buff != end) { + pos = memchr(buff, '\0', sizeof(lxb_char_t) * (end - buff)); + if (pos == NULL) { + break; + } + + res = lexbor_str_append(str, mraw, buff, (pos - buff)); + if (res == NULL) { + return NULL; + } + + res = lexbor_str_append(str, mraw, + lexbor_str_res_ansi_replacement_character, + sizeof(lexbor_str_res_ansi_replacement_character) - 1); + if (res == NULL) { + return NULL; + } + + buff = pos + 1; + } + + if (buff != end) { + res = lexbor_str_append(str, mraw, buff, (end - buff)); + if (res == NULL) { + return NULL; + } + } + + return &str->data[current_len]; +} + +lxb_char_t * +lexbor_str_copy(lexbor_str_t *dest, const lexbor_str_t *target, + lexbor_mraw_t *mraw) +{ + if (target->data == NULL) { + return NULL; + } + + if (dest->data == NULL) { + lexbor_str_init(dest, mraw, target->length); + + if (dest->data == NULL) { + return NULL; + } + } + + return lexbor_str_append(dest, mraw, target->data, target->length); +} + +void +lexbor_str_stay_only_whitespace(lexbor_str_t *target) +{ + size_t i, pos = 0; + lxb_char_t *data = target->data; + + for (i = 0; i < target->length; i++) { + if (lexbor_utils_whitespace(data[i], ==, ||)) { + data[pos] = data[i]; + pos++; + } + } + + target->length = pos; +} + +void +lexbor_str_strip_collapse_whitespace(lexbor_str_t *target) +{ + size_t i, offset, ws_i; + lxb_char_t *data = target->data; + + if (target->length == 0) { + return; + } + + if (lexbor_utils_whitespace(*data, ==, ||)) { + *data = 0x20; + } + + for (i = 0, offset = 0, ws_i = 0; i < target->length; i++) + { + if (lexbor_utils_whitespace(data[i], ==, ||)) { + if (data[ws_i] != 0x20) { + data[offset] = 0x20; + + ws_i = offset; + offset++; + } + } + else { + if (data[ws_i] == 0x20) { + ws_i = offset; + } + + data[offset] = data[i]; + offset++; + } + } + + if (offset != i) { + if (offset != 0) { + if (data[offset - 1] == 0x20) { + offset--; + } + } + + data[offset] = 0x00; + target->length = offset; + } +} + +size_t +lexbor_str_crop_whitespace_from_begin(lexbor_str_t *target) +{ + size_t i; + lxb_char_t *data = target->data; + + for (i = 0; i < target->length; i++) { + if (lexbor_utils_whitespace(data[i], !=, &&)) { + break; + } + } + + if (i != 0 && i != target->length) { + memmove(target->data, &target->data[i], (target->length - i)); + } + + target->length -= i; + return i; +} + +size_t +lexbor_str_whitespace_from_begin(lexbor_str_t *target) +{ + size_t i; + lxb_char_t *data = target->data; + + for (i = 0; i < target->length; i++) { + if (lexbor_utils_whitespace(data[i], !=, &&)) { + break; + } + } + + return i; +} + +size_t +lexbor_str_whitespace_from_end(lexbor_str_t *target) +{ + size_t i = target->length; + lxb_char_t *data = target->data; + + while (i) { + i--; + + if (lexbor_utils_whitespace(data[i], !=, &&)) { + return target->length - (i + 1); + } + } + + return 0; +} + +lxb_char_t * +lexbor_str_copy_to(lexbor_str_t *str, const lxb_char_t *buff, size_t length) +{ + lxb_char_t *data_begin; + + data_begin = &str->data[str->length]; + memcpy(data_begin, buff, sizeof(lxb_char_t) * length); + + str->length += length; + + return data_begin; +} + +lxb_char_t * +lexbor_str_copy_to_with_null(lexbor_str_t *str, + const lxb_char_t *buff, size_t length) +{ + lxb_char_t *data_begin = lexbor_str_copy_to(str, buff, length); + + str->data[str->length] = '\0'; + + return data_begin; +} + +/* + * Data utils + * TODO: All functions need optimization. + */ +const lxb_char_t * +lexbor_str_data_ncasecmp_first(const lxb_char_t *first, const lxb_char_t *sec, + size_t sec_size) +{ + size_t i; + + for (i = 0; i < sec_size; i++) { + if (first[i] == '\0') { + return &first[i]; + } + + if (lexbor_str_res_map_lowercase[ first[i] ] + != lexbor_str_res_map_lowercase[ sec[i] ]) + { + return NULL; + } + } + + return &first[i]; +} + +bool +lexbor_str_data_ncasecmp_end(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + while (size != 0) { + size--; + + if (lexbor_str_res_map_lowercase[ first[size] ] + != lexbor_str_res_map_lowercase[ sec[size] ]) + { + return false; + } + } + + return true; +} + +bool +lexbor_str_data_ncasecmp_contain(const lxb_char_t *where, size_t where_size, + const lxb_char_t *what, size_t what_size) +{ + for (size_t i = 0; what_size <= (where_size - i); i++) { + if(lexbor_str_data_ncasecmp(&where[i], what, what_size)) { + return true; + } + } + + return false; +} + +bool +lexbor_str_data_ncasecmp(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (lexbor_str_res_map_lowercase[ first[i] ] + != lexbor_str_res_map_lowercase[ sec[i] ]) + { + return false; + } + } + + return true; +} + +bool +lexbor_str_data_nlocmp_right(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (first[i] != lexbor_str_res_map_lowercase[ sec[i] ]) { + return false; + } + } + + return true; +} + +bool +lexbor_str_data_nupcmp_right(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + for (size_t i = 0; i < size; i++) { + if (first[i] != lexbor_str_res_map_uppercase[ sec[i] ]) { + return false; + } + } + + return true; +} + +bool +lexbor_str_data_casecmp(const lxb_char_t *first, const lxb_char_t *sec) +{ + for (;;) { + if (lexbor_str_res_map_lowercase[*first] + != lexbor_str_res_map_lowercase[*sec]) + { + return false; + } + + if (*first == '\0') { + return true; + } + + first++; + sec++; + } +} + +bool +lexbor_str_data_ncmp_end(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + while (size != 0) { + size--; + + if (first[size] != sec[size]) { + return false; + } + } + + return true; +} + +bool +lexbor_str_data_ncmp_contain(const lxb_char_t *where, size_t where_size, + const lxb_char_t *what, size_t what_size) +{ + for (size_t i = 0; what_size <= (where_size - i); i++) { + if(memcmp(&where[i], what, sizeof(lxb_char_t) * what_size) == 0) { + return true; + } + } + + return false; +} + +bool +lexbor_str_data_ncmp(const lxb_char_t *first, const lxb_char_t *sec, + size_t size) +{ + return memcmp(first, sec, sizeof(lxb_char_t) * size) == 0; +} + +bool +lexbor_str_data_cmp(const lxb_char_t *first, const lxb_char_t *sec) +{ + for (;;) { + if (*first != *sec) { + return false; + } + + if (*first == '\0') { + return true; + } + + first++; + sec++; + } +} + +bool +lexbor_str_data_cmp_ws(const lxb_char_t *first, const lxb_char_t *sec) +{ + for (;;) { + if (*first != *sec) { + return false; + } + + if (lexbor_utils_whitespace(*first, ==, ||) || *first == '\0') { + return true; + } + + first++; + sec++; + } +} + +void +lexbor_str_data_to_lowercase(lxb_char_t *to, const lxb_char_t *from, size_t len) +{ + while (len) { + len--; + + to[len] = lexbor_str_res_map_lowercase[ from[len] ]; + } +} + +void +lexbor_str_data_to_uppercase(lxb_char_t *to, const lxb_char_t *from, size_t len) +{ + while (len) { + len--; + + to[len] = lexbor_str_res_map_uppercase[ from[len] ]; + } +} + +const lxb_char_t * +lexbor_str_data_find_lowercase(const lxb_char_t *data, size_t len) +{ + while (len) { + len--; + + if (data[len] == lexbor_str_res_map_lowercase[ data[len] ]) { + return &data[len]; + } + } + + return NULL; +} + +const lxb_char_t * +lexbor_str_data_find_uppercase(const lxb_char_t *data, size_t len) +{ + while (len) { + len--; + + if (data[len] == lexbor_str_res_map_uppercase[ data[len] ]) { + return &data[len]; + } + } + + return NULL; +} + +/* + * No inline functions for ABI. + */ +lxb_char_t * +lexbor_str_data_noi(lexbor_str_t *str) +{ + return lexbor_str_data(str); +} + +size_t +lexbor_str_length_noi(lexbor_str_t *str) +{ + return lexbor_str_length(str); +} + +size_t +lexbor_str_size_noi(lexbor_str_t *str) +{ + return lexbor_str_size(str); +} + +void +lexbor_str_data_set_noi(lexbor_str_t *str, lxb_char_t *data) +{ + lexbor_str_data_set(str, data); +} + +lxb_char_t * +lexbor_str_length_set_noi(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t length) +{ + return lexbor_str_length_set(str, mraw, length); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.h new file mode 100644 index 0000000..8e5f380 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2018-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_STR_H +#define LEXBOR_STR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" +#include "lexbor/core/mraw.h" +#include "lexbor/core/utils.h" + + +#define lexbor_str_get(str, attr) str->attr +#define lexbor_str_set(str, attr) lexbor_str_get(str, attr) +#define lexbor_str_len(str) lexbor_str_get(str, length) +#define lexbor_str(p) {.data = (lxb_char_t *) (p), sizeof(p) - 1} + +#define lexbor_str_check_size_arg_m(str, size, mraw, plus_len, return_fail) \ + do { \ + void *tmp; \ + \ + if (str->length > (SIZE_MAX - (plus_len))) \ + return (return_fail); \ + \ + if ((str->length + (plus_len)) > (size)) { \ + tmp = lexbor_mraw_realloc(mraw, str->data, \ + (str->length + plus_len)); \ + \ + if (tmp == NULL) { \ + return (return_fail); \ + } \ + \ + str->data = (lxb_char_t *) tmp; \ + } \ + } \ + while (0) + + +typedef struct { + lxb_char_t *data; + size_t length; +} +lexbor_str_t; + + +LXB_API lexbor_str_t * +lexbor_str_create(void); + +LXB_API lxb_char_t * +lexbor_str_init(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t size); + +LXB_API lxb_char_t * +lexbor_str_init_append(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *data, size_t length); + +LXB_API void +lexbor_str_clean(lexbor_str_t *str); + +LXB_API void +lexbor_str_clean_all(lexbor_str_t *str); + +LXB_API lexbor_str_t * +lexbor_str_destroy(lexbor_str_t *str, lexbor_mraw_t *mraw, bool destroy_obj); + + +LXB_API lxb_char_t * +lexbor_str_realloc(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t new_size); + +LXB_API lxb_char_t * +lexbor_str_check_size(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t plus_len); + +/* Append */ +LXB_API lxb_char_t * +lexbor_str_append(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *data, size_t length); + +LXB_API lxb_char_t * +lexbor_str_append_before(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *buff, size_t length); + +LXB_API lxb_char_t * +lexbor_str_append_one(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t data); + +LXB_API lxb_char_t * +lexbor_str_append_lowercase(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *data, size_t length); + +LXB_API lxb_char_t * +lexbor_str_append_with_rep_null_chars(lexbor_str_t *str, lexbor_mraw_t *mraw, + const lxb_char_t *buff, size_t length); + +/* Other functions */ +LXB_API lxb_char_t * +lexbor_str_copy(lexbor_str_t *dest, const lexbor_str_t *target, + lexbor_mraw_t *mraw); + +LXB_API void +lexbor_str_stay_only_whitespace(lexbor_str_t *target); + +LXB_API void +lexbor_str_strip_collapse_whitespace(lexbor_str_t *target); + +LXB_API size_t +lexbor_str_crop_whitespace_from_begin(lexbor_str_t *target); + +LXB_API size_t +lexbor_str_whitespace_from_begin(lexbor_str_t *target); + +LXB_API size_t +lexbor_str_whitespace_from_end(lexbor_str_t *target); + +LXB_API lxb_char_t * +lexbor_str_copy_to(lexbor_str_t *str, const lxb_char_t *buff, size_t length); + +LXB_API lxb_char_t * +lexbor_str_copy_to_with_null(lexbor_str_t *str, + const lxb_char_t *buff, size_t length); + +/* Data utils */ +/* + * [in] first: must be null-terminated + * [in] sec: no matter what data + * [in] sec_size: size of the 'sec' buffer + * + * Function compare two lxb_char_t data until find '\0' in first arg. + * Successfully if the function returned a pointer starting with '\0', + * otherwise, if the data of the second buffer is insufficient function returned + * position in first buffer. + * If function returns NULL, the data are not equal. + */ +LXB_API const lxb_char_t * +lexbor_str_data_ncasecmp_first(const lxb_char_t *first, const lxb_char_t *sec, + size_t sec_size); +LXB_API bool +lexbor_str_data_ncasecmp_end(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); +LXB_API bool +lexbor_str_data_ncasecmp_contain(const lxb_char_t *where, size_t where_size, + const lxb_char_t *what, size_t what_size); +LXB_API bool +lexbor_str_data_ncasecmp(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); +LXB_API bool +lexbor_str_data_nlocmp_right(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); +LXB_API bool +lexbor_str_data_nupcmp_right(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); +LXB_API bool +lexbor_str_data_casecmp(const lxb_char_t *first, const lxb_char_t *sec); + +LXB_API bool +lexbor_str_data_ncmp_end(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); +LXB_API bool +lexbor_str_data_ncmp_contain(const lxb_char_t *where, size_t where_size, + const lxb_char_t *what, size_t what_size); +LXB_API bool +lexbor_str_data_ncmp(const lxb_char_t *first, const lxb_char_t *sec, + size_t size); + +LXB_API bool +lexbor_str_data_cmp(const lxb_char_t *first, const lxb_char_t *sec); + +LXB_API bool +lexbor_str_data_cmp_ws(const lxb_char_t *first, const lxb_char_t *sec); + +LXB_API void +lexbor_str_data_to_lowercase(lxb_char_t *to, const lxb_char_t *from, size_t len); + +LXB_API void +lexbor_str_data_to_uppercase(lxb_char_t *to, const lxb_char_t *from, size_t len); + +LXB_API const lxb_char_t * +lexbor_str_data_find_lowercase(const lxb_char_t *data, size_t len); + +LXB_API const lxb_char_t * +lexbor_str_data_find_uppercase(const lxb_char_t *data, size_t len); + + +/* + * Inline functions + */ +lxb_inline lxb_char_t * +lexbor_str_data(lexbor_str_t *str) +{ + return str->data; +} + +lxb_inline size_t +lexbor_str_length(lexbor_str_t *str) +{ + return str->length; +} + +lxb_inline size_t +lexbor_str_size(lexbor_str_t *str) +{ + return lexbor_mraw_data_size(str->data); +} + +lxb_inline void +lexbor_str_data_set(lexbor_str_t *str, lxb_char_t *data) +{ + str->data = data; +} + +lxb_inline lxb_char_t * +lexbor_str_length_set(lexbor_str_t *str, lexbor_mraw_t *mraw, size_t length) +{ + if (length >= lexbor_str_size(str)) { + lxb_char_t *tmp; + + tmp = lexbor_str_realloc(str, mraw, length + 1); + if (tmp == NULL) { + return NULL; + } + } + + str->length = length; + str->data[length] = 0x00; + + return str->data; +} + +/* + * No inline functions for ABI. + */ +LXB_API lxb_char_t * +lexbor_str_data_noi(lexbor_str_t *str); + +LXB_API size_t +lexbor_str_length_noi(lexbor_str_t *str); + +LXB_API size_t +lexbor_str_size_noi(lexbor_str_t *str); + +LXB_API void +lexbor_str_data_set_noi(lexbor_str_t *str, lxb_char_t *data); + +LXB_API lxb_char_t * +lexbor_str_length_set_noi(lexbor_str_t *str, lexbor_mraw_t *mraw, + size_t length); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_STR_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str_res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str_res.h new file mode 100644 index 0000000..be34a0e --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/str_res.h @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + + +LXB_API const lxb_char_t +lexbor_str_res_ansi_replacement_character[4] = "\xEF\xBF\xBD"; + +LXB_API const lxb_char_t +lexbor_str_res_map_num[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +LXB_API const lxb_char_t +lexbor_str_res_map_hex[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +LXB_API const lxb_char_t +lexbor_str_res_map_lowercase[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, + 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, + 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, + 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff +}; + +LXB_API const lxb_char_t +lexbor_str_res_map_uppercase[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, + 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, + 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, + 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x41, 0x42, + 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, + 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, + 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, + 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, + 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, + 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff +}; + +LXB_API const size_t +lexbor_str_res_replacement_character[160] = +{ + 65533, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 91, 92, 93, 94, 95, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 120, 121, 122, 123, 124, 125, + 126, 127, 8364, 129, 8218, 402, 8222, 8230, 8224, + 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, + 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, + 8482, 353, 8250, 339, 157, 382, 376 +}; + +LXB_API const size_t +lexbor_str_res_alphanumeric_character[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, + 0x06, 0x07, 0x08, 0x09, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +LXB_API const size_t +lexbor_str_res_alpha_character[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, + 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, + 0x0e, 0x0f, 0x0a, 0x0b, 0x0c, 0x0d, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +LXB_API const unsigned char +lexbor_tokenizer_chars_map[256] = +{ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x02, 0x02, 0xff, 0x02, 0x02, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x02, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff +}; + +LXB_API const lxb_char_t +lexbor_str_res_map_hex_to_char[17] = +{ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x00 +}; + +LXB_API const lxb_char_t +lexbor_str_res_map_hex_to_char_lowercase[17] = +{ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x00 +}; + +LXB_API const char * +lexbor_str_res_char_to_two_hex_value[257] = +{ + "00", "01", "02", "03", "04", "05", "06", "07", + "08", "09", "0A", "0B", "0C", "0D", "0E", "0F", + "10", "11", "12", "13", "14", "15", "16", "17", + "18", "19", "1A", "1B", "1C", "1D", "1E", "1F", + "20", "21", "22", "23", "24", "25", "26", "27", + "28", "29", "2A", "2B", "2C", "2D", "2E", "2F", + "30", "31", "32", "33", "34", "35", "36", "37", + "38", "39", "3A", "3B", "3C", "3D", "3E", "3F", + "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "4A", "4B", "4C", "4D", "4E", "4F", + "50", "51", "52", "53", "54", "55", "56", "57", + "58", "59", "5A", "5B", "5C", "5D", "5E", "5F", + "60", "61", "62", "63", "64", "65", "66", "67", + "68", "69", "6A", "6B", "6C", "6D", "6E", "6F", + "70", "71", "72", "73", "74", "75", "76", "77", + "78", "79", "7A", "7B", "7C", "7D", "7E", "7F", + "80", "81", "82", "83", "84", "85", "86", "87", + "88", "89", "8A", "8B", "8C", "8D", "8E", "8F", + "90", "91", "92", "93", "94", "95", "96", "97", + "98", "99", "9A", "9B", "9C", "9D", "9E", "9F", + "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", + "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF", + "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", + "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF", + "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", + "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF", + "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", + "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", + "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF", + NULL +}; + +LXB_API const char * +lexbor_str_res_char_to_two_hex_value_lowercase[257] = +{ + "00", "01", "02", "03", "04", "05", "06", "07", + "08", "09", "0a", "0b", "0c", "0d", "0e", "0f", + "10", "11", "12", "13", "14", "15", "16", "17", + "18", "19", "1a", "1b", "1c", "1d", "1e", "1f", + "20", "21", "22", "23", "24", "25", "26", "27", + "28", "29", "2a", "2b", "2c", "2d", "2e", "2f", + "30", "31", "32", "33", "34", "35", "36", "37", + "38", "39", "3a", "3b", "3c", "3d", "3e", "3f", + "40", "41", "42", "43", "44", "45", "46", "47", + "48", "49", "4a", "4b", "4c", "4d", "4e", "4f", + "50", "51", "52", "53", "54", "55", "56", "57", + "58", "59", "5a", "5b", "5c", "5d", "5e", "5f", + "60", "61", "62", "63", "64", "65", "66", "67", + "68", "69", "6a", "6b", "6c", "6d", "6e", "6f", + "70", "71", "72", "73", "74", "75", "76", "77", + "78", "79", "7a", "7b", "7c", "7d", "7e", "7f", + "80", "81", "82", "83", "84", "85", "86", "87", + "88", "89", "8a", "8b", "8c", "8d", "8e", "8f", + "90", "91", "92", "93", "94", "95", "96", "97", + "98", "99", "9a", "9b", "9c", "9d", "9e", "9f", + "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", + "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af", + "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", + "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf", + "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", + "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf", + "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", + "d8", "d9", "da", "db", "dc", "dd", "de", "df", + "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", + "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff", + NULL +}; diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.c new file mode 100644 index 0000000..2dad45c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.c @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + */ + +#include +#include + +#include "lexbor/core/diyfp.h" +#include "lexbor/core/strtod.h" + + +/* + * Max double: 1.7976931348623157 x 10^308 + * Min non-zero double: 4.9406564584124654 x 10^-324 + * Any x >= 10^309 is interpreted as +infinity. + * Any x <= 10^-324 is interpreted as 0. + * Note that 2.5e-324 (despite being smaller than the min double) + * will be read as non-zero (equal to the min non-zero double). + */ + +#define LEXBOR_DECIMAL_POWER_MAX 309 +#define LEXBOR_DECIMAL_POWER_MIN (-324) + +#define LEXBOR_UINT64_MAX lexbor_uint64_hl(0xFFFFFFFF, 0xFFFFFFFF) +#define LEXBOR_UINT64_DECIMAL_DIGITS_MAX 19 + +#define LEXBOR_DENOM_LOG 3 +#define LEXBOR_DENOM (1 << LEXBOR_DENOM_LOG) + + +static lexbor_diyfp_t +lexbor_strtod_diyfp_read(const lxb_char_t *start, size_t length, + int *remaining); + +static double +lexbor_strtod_diyfp_strtod(const lxb_char_t *start, size_t length, int exp); + + +/* + * Reads digits from the buffer and converts them to a uint64. + * Reads in as many digits as fit into a uint64. + * When the string starts with "1844674407370955161" no further digit is read. + * Since 2^64 = 18446744073709551616 it would still be possible read another + * digit if it was less or equal than 6, but this would complicate the code. + */ +lxb_inline uint64_t +lexbor_strtod_read_uint64(const lxb_char_t *start, size_t length, + size_t *ndigits) +{ + lxb_char_t d; + uint64_t value; + const lxb_char_t *p, *e; + + value = 0; + + p = start; + e = p + length; + + while (p < e && value <= (UINT64_MAX / 10 - 1)) { + d = *p++ - '0'; + value = 10 * value + d; + } + + *ndigits = p - start; + + return value; +} + +/* + * Reads a nxt_diyfp_t from the buffer. + * The returned nxt_diyfp_t is not necessarily normalized. + * If remaining is zero then the returned nxt_diyfp_t is accurate. + * Otherwise it has been rounded and has error of at most 1/2 ulp. + */ +static lexbor_diyfp_t +lexbor_strtod_diyfp_read(const lxb_char_t *start, size_t length, int *remaining) +{ + size_t read; + uint64_t significand; + + significand = lexbor_strtod_read_uint64(start, length, &read); + + /* Round the significand. */ + + if (length != read) { + if (start[read] >= '5') { + significand++; + } + } + + *remaining = (int) (length - read); + + return lexbor_diyfp(significand, 0); +} + +/* + * Returns 10^exp as an exact nxt_diyfp_t. + * The given exp must be in the range [1; NXT_DECIMAL_EXPONENT_DIST[. + */ +lxb_inline lexbor_diyfp_t +lexbor_strtod_adjust_pow10(int exp) +{ + switch (exp) { + case 1: + return lexbor_diyfp(lexbor_uint64_hl(0xa0000000, 00000000), -60); + case 2: + return lexbor_diyfp(lexbor_uint64_hl(0xc8000000, 00000000), -57); + case 3: + return lexbor_diyfp(lexbor_uint64_hl(0xfa000000, 00000000), -54); + case 4: + return lexbor_diyfp(lexbor_uint64_hl(0x9c400000, 00000000), -50); + case 5: + return lexbor_diyfp(lexbor_uint64_hl(0xc3500000, 00000000), -47); + case 6: + return lexbor_diyfp(lexbor_uint64_hl(0xf4240000, 00000000), -44); + case 7: + return lexbor_diyfp(lexbor_uint64_hl(0x98968000, 00000000), -40); + default: + return lexbor_diyfp(0, 0); + } +} + +/* + * Returns the significand size for a given order of magnitude. + * If v = f*2^e with 2^p-1 <= f <= 2^p then p+e is v's order of magnitude. + * This function returns the number of significant binary digits v will have + * once its encoded into a double. In almost all cases this is equal to + * NXT_SIGNIFICAND_SIZE. The only exception are denormals. They start with + * leading zeroes and their effective significand-size is hence smaller. + */ +lxb_inline int +lexbor_strtod_diyfp_sgnd_size(int order) +{ + if (order >= (LEXBOR_DBL_EXPONENT_DENORMAL + LEXBOR_SIGNIFICAND_SIZE)) { + return LEXBOR_SIGNIFICAND_SIZE; + } + + if (order <= LEXBOR_DBL_EXPONENT_DENORMAL) { + return 0; + } + + return order - LEXBOR_DBL_EXPONENT_DENORMAL; +} + +/* + * Returns either the correct double or the double that is just below + * the correct double. + */ +static double +lexbor_strtod_diyfp_strtod(const lxb_char_t *start, size_t length, int exp) +{ + int magnitude, prec_digits; + int remaining, dec_exp, adj_exp, orig_e, shift; + int64_t error; + uint64_t prec_bits, half_way; + lexbor_diyfp_t value, pow, adj_pow, rounded; + + value = lexbor_strtod_diyfp_read(start, length, &remaining); + + exp += remaining; + + /* + * Since some digits may have been dropped the value is not accurate. + * If remaining is different than 0 than the error is at most .5 ulp + * (unit in the last place). + * Using a common denominator to avoid dealing with fractions. + */ + + error = (remaining == 0 ? 0 : LEXBOR_DENOM / 2); + + orig_e = value.exp; + value = lexbor_diyfp_normalize(value); + error <<= orig_e - value.exp; + + if (exp < LEXBOR_DECIMAL_EXPONENT_MIN) { + return 0.0; + } + + pow = lexbor_cached_power_dec(exp, &dec_exp); + + if (dec_exp != exp) { + adj_exp = exp - dec_exp; + adj_pow = lexbor_strtod_adjust_pow10(exp - dec_exp); + value = lexbor_diyfp_mul(value, adj_pow); + + if (LEXBOR_UINT64_DECIMAL_DIGITS_MAX - (int) length < adj_exp) { + /* + * The adjustment power is exact. There is hence only + * an error of 0.5. + */ + error += LEXBOR_DENOM / 2; + } + } + + value = lexbor_diyfp_mul(value, pow); + + /* + * The error introduced by a multiplication of a * b equals + * error_a + error_b + error_a * error_b / 2^64 + 0.5 + * Substituting a with 'value' and b with 'pow': + * error_b = 0.5 (all cached powers have an error of less than 0.5 ulp), + * error_ab = 0 or 1 / NXT_DENOM > error_a * error_b / 2^64. + */ + + error += LEXBOR_DENOM + (error != 0 ? 1 : 0); + + orig_e = value.exp; + value = lexbor_diyfp_normalize(value); + error <<= orig_e - value.exp; + + /* + * Check whether the double's significand changes when the error is added + * or subtracted. + */ + + magnitude = LEXBOR_DIYFP_SIGNIFICAND_SIZE + value.exp; + prec_digits = LEXBOR_DIYFP_SIGNIFICAND_SIZE + - lexbor_strtod_diyfp_sgnd_size(magnitude); + + if (prec_digits + LEXBOR_DENOM_LOG >= LEXBOR_DIYFP_SIGNIFICAND_SIZE) { + /* + * This can only happen for very small denormals. In this case the + * half-way multiplied by the denominator exceeds the range of uint64. + * Simply shift everything to the right. + */ + shift = prec_digits + LEXBOR_DENOM_LOG + - LEXBOR_DIYFP_SIGNIFICAND_SIZE + 1; + + value = lexbor_diyfp_shift_right(value, shift); + + /* + * Add 1 for the lost precision of error, and NXT_DENOM + * for the lost precision of value.significand. + */ + error = (error >> shift) + 1 + LEXBOR_DENOM; + prec_digits -= shift; + } + + prec_bits = value.significand & (((uint64_t) 1 << prec_digits) - 1); + prec_bits *= LEXBOR_DENOM; + + half_way = (uint64_t) 1 << (prec_digits - 1); + half_way *= LEXBOR_DENOM; + + rounded = lexbor_diyfp_shift_right(value, prec_digits); + + if (prec_bits >= half_way + error) { + rounded.significand++; + } + + return lexbor_diyfp_2d(rounded); +} + +double +lexbor_strtod_internal(const lxb_char_t *start, size_t length, int exp) +{ + size_t left, right; + const lxb_char_t *p, *e, *b; + + /* Trim leading zeroes. */ + + p = start; + e = p + length; + + while (p < e) { + if (*p != '0') { + start = p; + break; + } + + p++; + } + + left = e - p; + + /* Trim trailing zeroes. */ + + b = start; + p = b + left - 1; + + while (p > b) { + if (*p != '0') { + break; + } + + p--; + } + + right = p - b + 1; + + length = right; + + if (length == 0) { + return 0.0; + } + + exp += (int) (left - right); + + if (exp + (int) length - 1 >= LEXBOR_DECIMAL_POWER_MAX) { + return INFINITY; + } + + if (exp + (int) length <= LEXBOR_DECIMAL_POWER_MIN) { + return 0.0; + } + + return lexbor_strtod_diyfp_strtod(start, length, exp); +} + +#undef LEXBOR_DECIMAL_POWER_MAX +#undef LEXBOR_DECIMAL_POWER_MIN + +#undef LEXBOR_UINT64_MAX +#undef LEXBOR_UINT64_DECIMAL_DIGITS_MAX + +#undef LEXBOR_DENOM_LOG +#undef LEXBOR_DENOM diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.h new file mode 100644 index 0000000..0f834ba --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/strtod.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015-2019 NGINX, Inc. + * Copyright (C) 2019-2025 F5, Inc. + * Copyright (C) 2015-2021 Igor Sysoev + * Copyright (C) 2017-2025 Dmitry Volyntsev + * Copyright (C) 2019-2022 Alexander Borisov + * Copyright (C) 2022-2025 Vadim Zhestikov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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 AUTHOR 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 AUTHOR 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. + */ + +/* + * Copyright (C) Alexander Borisov + */ + +#ifndef LEXBOR_STRTOD_H +#define LEXBOR_STRTOD_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +LXB_API double +lexbor_strtod_internal(const lxb_char_t *start, size_t length, int exp); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_STRTOD_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/swar.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/swar.h new file mode 100644 index 0000000..ec0a13b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/swar.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 Alexander Borisov + * + * Author: Niels Dossche + */ + +#ifndef LEXBOR_SWAR_H +#define LEXBOR_SWAR_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/core/base.h" + + +/* + * Based on techniques from https://graphics.stanford.edu/~seander/bithacks.html + */ +#define LEXBOR_SWAR_ONES (~((size_t) 0) / 0xFF) +#define LEXBOR_SWAR_REPEAT(x) (LEXBOR_SWAR_ONES * (x)) +#define LEXBOR_SWAR_HAS_ZERO(v) (((v) - LEXBOR_SWAR_ONES) & ~(v) & LEXBOR_SWAR_REPEAT(0x80)) +#define LEXBOR_SWAR_IS_LITTLE_ENDIAN (*(unsigned char *) &(uint16_t){1}) + + +/* + * When handling hot loops that search for a set of characters, + * this function can be used to quickly move the data pointer much + * closer to the first occurrence of such a character. + */ +lxb_inline const lxb_char_t * +lexbor_swar_seek4(const lxb_char_t *data, const lxb_char_t *end, + lxb_char_t c1, lxb_char_t c2, lxb_char_t c3, lxb_char_t c4) +{ + size_t bytes, matches, t1, t2, t3, t4; + + if (LEXBOR_SWAR_IS_LITTLE_ENDIAN) { + while (data + sizeof(size_t) <= end) { + memcpy(&bytes, data, sizeof(size_t)); + + t1 = bytes ^ LEXBOR_SWAR_REPEAT(c1); + t2 = bytes ^ LEXBOR_SWAR_REPEAT(c2); + t3 = bytes ^ LEXBOR_SWAR_REPEAT(c3); + t4 = bytes ^ LEXBOR_SWAR_REPEAT(c4); + matches = LEXBOR_SWAR_HAS_ZERO(t1) | LEXBOR_SWAR_HAS_ZERO(t2) + | LEXBOR_SWAR_HAS_ZERO(t3) | LEXBOR_SWAR_HAS_ZERO(t4); + + if (matches) { + data += ((((matches - 1) & LEXBOR_SWAR_ONES) * LEXBOR_SWAR_ONES) + >> (sizeof(size_t) * 8 - 8)) - 1; + break; + } else { + data += sizeof(size_t); + } + } + } + + return data; +} + +lxb_inline const lxb_char_t * +lexbor_swar_seek3(const lxb_char_t *data, const lxb_char_t *end, + lxb_char_t c1, lxb_char_t c2, lxb_char_t c3) +{ + size_t bytes, matches, t1, t2, t3; + + if (LEXBOR_SWAR_IS_LITTLE_ENDIAN) { + while (data + sizeof(size_t) <= end) { + memcpy(&bytes, data, sizeof(size_t)); + + t1 = bytes ^ LEXBOR_SWAR_REPEAT(c1); + t2 = bytes ^ LEXBOR_SWAR_REPEAT(c2); + t3 = bytes ^ LEXBOR_SWAR_REPEAT(c3); + matches = LEXBOR_SWAR_HAS_ZERO(t1) | LEXBOR_SWAR_HAS_ZERO(t2) + | LEXBOR_SWAR_HAS_ZERO(t3); + + if (matches) { + data += ((((matches - 1) & LEXBOR_SWAR_ONES) * LEXBOR_SWAR_ONES) + >> (sizeof(size_t) * 8 - 8)) - 1; + break; + } else { + data += sizeof(size_t); + } + } + } + + return data; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_SWAR_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/types.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/types.h new file mode 100644 index 0000000..00f82ad --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/types.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_TYPES_H +#define LEXBOR_TYPES_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/* Inline */ +#ifdef _MSC_VER + #define lxb_inline static __forceinline +#else + #define lxb_inline static inline +#endif + + +/* Simple types */ +typedef uint32_t lxb_codepoint_t; +typedef unsigned char lxb_char_t; +typedef unsigned int lxb_status_t; + +/* Callbacks */ +typedef lxb_status_t (*lexbor_callback_f)(const lxb_char_t *buffer, + size_t size, void *ctx); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_TYPES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.c new file mode 100644 index 0000000..9ca393a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/utils.h" + + +size_t +lexbor_utils_power(size_t t, size_t k) +{ + size_t res = 1; + + while (k) { + if (k & 1) { + res *= t; + } + + t *= t; + k >>= 1; + } + + return res; +} + +size_t +lexbor_utils_hash_hash(const lxb_char_t *key, size_t key_size) +{ + size_t hash, i; + + for (hash = i = 0; i < key_size; i++) { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + + return hash; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.h new file mode 100644 index 0000000..f0ebf16 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/core/utils.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_UTILS_H +#define LEXBOR_UTILS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" + + +#define lexbor_utils_whitespace(onechar, action, logic) \ + (onechar action ' ' logic \ + onechar action '\t' logic \ + onechar action '\n' logic \ + onechar action '\f' logic \ + onechar action '\r') + + +LXB_API size_t +lexbor_utils_power(size_t t, size_t k); + +LXB_API size_t +lexbor_utils_hash_hash(const lxb_char_t *key, size_t key_size); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_UTILS_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.c new file mode 100644 index 0000000..6c5435d --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/at_rule.h" +#include "lexbor/css/css.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/stylesheet.h" +#include "lexbor/css/at_rule/state.h" +#include "lexbor/css/at_rule/types.h" +#include "lexbor/css/at_rule/res.h" +#include "lexbor/core/serialize.h" + + +const lxb_css_entry_at_rule_data_t * +lxb_css_at_rule_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_at_rule_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_entry_at_rule_data_t * +lxb_css_at_rule_by_id(uintptr_t id) +{ + return &lxb_css_at_rule_data[id]; +} + +lxb_css_rule_at_t * +lxb_css_at_rule_create(lxb_css_parser_t *parser, + const lxb_char_t *name, size_t length, + const lxb_css_entry_at_rule_data_t **out_entry) +{ + void *prop; + const lxb_css_entry_at_rule_data_t *entry; + lxb_css_at_rule__custom_t *custom; + lxb_css_rule_at_t *at; + + at = lxb_css_rule_at_create(parser->memory); + if (at == NULL) { + return NULL; + } + + entry = lxb_css_at_rule_by_name(name, length); + if (entry == NULL) { + entry = lxb_css_at_rule_by_id(LXB_CSS_AT_RULE__CUSTOM); + + prop = entry->create(parser->memory); + if (prop == NULL) { + goto failed; + } + + custom = prop; + + (void) lexbor_str_init(&custom->name, parser->memory->mraw, length); + if (custom->name.data == NULL) { + goto failed; + } + + memcpy(custom->name.data, name, length); + + custom->name.length = length; + custom->name.data[custom->name.length] = 0x00; + } + else { + prop = entry->create(parser->memory); + if (prop == NULL) { + goto failed; + } + } + + at->type = entry->unique; + at->u.user = prop; + + if (out_entry != NULL) { + *out_entry = entry; + } + + return at; + +failed: + + if (prop != NULL) { + (void) entry->destroy(parser->memory, prop, true); + } + + if (at != NULL) { + (void) lxb_css_rule_at_destroy(at, true); + } + + if (out_entry != NULL) { + *out_entry = NULL; + } + + return NULL; +} + +void * +lxb_css_at_rule_destroy(lxb_css_memory_t *memory, void *value, + lxb_css_at_rule_type_t type, bool self_destroy) +{ + const lxb_css_entry_at_rule_data_t *data; + + data = lxb_css_at_rule_by_id(type); + if (data == NULL) { + return value; + } + + return data->destroy(memory, value, self_destroy); +} + +lxb_status_t +lxb_css_at_rule_convert_to_undef(lxb_css_parser_t *parser, + lxb_css_rule_at_t *at) +{ + lxb_css_at_rule__undef_t *undef; + + undef = lxb_css_at_rule__undef_create(parser->memory); + if (undef == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + undef->type = at->type; + + (void) lxb_css_at_rule_destroy(parser->memory, at, undef->type, false); + + at->type = LXB_CSS_AT_RULE__UNDEF; + at->u.undef = undef; + + return lxb_css_make_data(parser, &at->u.undef->prelude, + at->prelude_begin, at->prelude_end); +} + +lxb_status_t +lxb_css_at_rule_serialize(const void *style, lxb_css_at_rule_type_t type, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_entry_at_rule_data_t *data; + + data = lxb_css_at_rule_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return data->serialize(style, cb, ctx); +} + +lxb_status_t +lxb_css_at_rule_serialize_str(const void *style, lxb_css_at_rule_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str) +{ + const lxb_css_entry_at_rule_data_t *data; + + data = lxb_css_at_rule_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return lxb_css_serialize_str_handler(style, str, mraw, data->serialize); +} + +lxb_status_t +lxb_css_at_rule_serialize_name(const void *style, lxb_css_at_rule_type_t type, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_entry_at_rule_data_t *data; + + if (type == LXB_CSS_AT_RULE__UNDEF) { + return lxb_css_at_rule__undef_serialize_name(style, cb, ctx); + } + else if (type == LXB_CSS_AT_RULE__CUSTOM) { + return lxb_css_at_rule__custom_serialize_name(style, cb, ctx); + } + + data = lxb_css_at_rule_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return cb(data->name, data->length, ctx); +} + +lxb_status_t +lxb_css_at_rule_serialize_name_str(const void *style, lxb_css_at_rule_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str) +{ + const lxb_css_entry_at_rule_data_t *data; + + if (type == LXB_CSS_AT_RULE__UNDEF) { + return lxb_css_serialize_str_handler(style, str, mraw, + lxb_css_at_rule__undef_serialize_name); + } + else if (type == LXB_CSS_AT_RULE__CUSTOM) { + return lxb_css_serialize_str_handler(style, str, mraw, + lxb_css_at_rule__custom_serialize_name); + } + + data = lxb_css_at_rule_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (str->data == NULL) { + lexbor_str_init(str, mraw, data->length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + (void) lexbor_str_append(str, mraw, data->name, data->length); + + return LXB_STATUS_OK; +} + +/* _undef. */ + +void * +lxb_css_at_rule__undef_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_at_rule__undef_t)); +} + +void * +lxb_css_at_rule__undef_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + if (style == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_mraw_free(memory->mraw, style); + } + + return style; +} + +lxb_status_t +lxb_css_at_rule__undef_serialize(const void *at, lexbor_serialize_cb_f cb, + void *ctx) +{ + lxb_status_t status; + const lxb_css_entry_at_rule_data_t *data; + const lxb_css_at_rule__undef_t *undef = at; + + static const lxb_char_t wc_str[] = " "; + static const lxb_char_t lb_str[] = "{"; + static const lxb_char_t rb_str[] = "}"; + static const lxb_char_t sm_str[] = ";"; + + data = lxb_css_at_rule_by_id(undef->type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (undef->prelude.data != NULL) { + lexbor_serialize_write(cb, wc_str, (sizeof(wc_str) - 1), ctx, status); + lexbor_serialize_write(cb, undef->prelude.data, undef->prelude.length, + ctx, status); + } + + if (undef->block != NULL) { + lexbor_serialize_write(cb, lb_str, (sizeof(lb_str) - 1), ctx, status); + + status = lxb_css_rule_list_serialize(undef->block, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, rb_str, (sizeof(rb_str) - 1), ctx, status); + } + else { + lexbor_serialize_write(cb, sm_str, (sizeof(sm_str) - 1), ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_at_rule__undef_serialize_name(const void *at, lexbor_serialize_cb_f cb, + void *ctx) +{ + const lxb_css_entry_at_rule_data_t *data; + const lxb_css_at_rule__undef_t *undef = at; + + data = lxb_css_at_rule_by_id(undef->type); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return cb(data->name, data->length, ctx); +} + +/* _custom. */ + +void * +lxb_css_at_rule__custom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_at_rule__custom_t)); +} + +void * +lxb_css_at_rule__custom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + if (style == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_mraw_free(memory->mraw, style); + } + + return style; +} + +lxb_status_t +lxb_css_at_rule__custom_serialize(const void *at, lexbor_serialize_cb_f cb, + void *ctx) +{ + lxb_status_t status; + const lxb_css_at_rule__custom_t *custom = at; + + static const lxb_char_t ws_str[] = " "; + static const lxb_char_t lb_str[] = "{"; + static const lxb_char_t rb_str[] = "}"; + + + if (custom->prelude.data != NULL) { + lexbor_serialize_write(cb, ws_str, (sizeof(ws_str) - 1), ctx, status); + lexbor_serialize_write(cb, custom->prelude.data, custom->prelude.length, + ctx, status); + } + + if (custom->block != NULL) { + lexbor_serialize_write(cb, lb_str, (sizeof(lb_str) - 1), ctx, status); + + status = lxb_css_rule_list_serialize(custom->block, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, rb_str, (sizeof(rb_str) - 1), ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_at_rule__custom_serialize_name(const void *at, lexbor_serialize_cb_f cb, + void *ctx) +{ + const lxb_css_at_rule__custom_t *custom = at; + + return cb(custom->name.data, custom->name.length, ctx); +} + +/* Media. */ + +void * +lxb_css_at_rule_media_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_at_rule_media_t)); +} + +void * +lxb_css_at_rule_media_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_at_rule__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_at_rule_media_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx) +{ + return LXB_STATUS_OK; +} + +/* Namespace. */ + +void * +lxb_css_at_rule_namespace_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_at_rule_namespace_t)); +} + +void * +lxb_css_at_rule_namespace_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_at_rule__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_at_rule_namespace_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx) +{ + return LXB_STATUS_OK; +} + +/* Font-face. */ + +void * +lxb_css_at_rule_font_face_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_at_rule_font_face_t)); +} + +void * +lxb_css_at_rule_font_face_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_at_rule__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_at_rule_font_face_serialize(const void *font_face, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_at_rule_font_face_t *ff = font_face; + + static const lxb_char_t lb_str[] = " {"; + static const lxb_char_t rb_str[] = "}"; + + if (ff->block != NULL) { + lexbor_serialize_write(cb, lb_str, (sizeof(lb_str) - 1), ctx, status); + + status = lxb_css_rule_list_serialize(ff->block, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, rb_str, (sizeof(rb_str) - 1), ctx, status); + } + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.h new file mode 100644 index 0000000..cacfa4c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule.h @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_AT_RULE_H +#define LXB_CSS_AT_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/at_rule/const.h" + + +typedef struct { + lxb_css_at_rule_type_t type; + lexbor_str_t prelude; + lxb_css_rule_list_t *block; +} +lxb_css_at_rule__undef_t; + +typedef struct { + lexbor_str_t name; + lexbor_str_t prelude; + lxb_css_rule_list_t *block; +} +lxb_css_at_rule__custom_t; + +typedef struct { + lxb_css_rule_list_t *block; +} +lxb_css_at_rule_media_t; + +typedef struct { + uintptr_t reserved; +} +lxb_css_at_rule_namespace_t; + +typedef struct { + lxb_css_rule_list_t *block; +} +lxb_css_at_rule_font_face_t; + + +LXB_API const lxb_css_entry_at_rule_data_t * +lxb_css_at_rule_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_entry_at_rule_data_t * +lxb_css_at_rule_by_id(uintptr_t id); + +LXB_API lxb_css_rule_at_t * +lxb_css_at_rule_create(lxb_css_parser_t *parser, + const lxb_char_t *name, size_t length, + const lxb_css_entry_at_rule_data_t **out_entry); + +LXB_API void * +lxb_css_at_rule_destroy(lxb_css_memory_t *memory, void *value, + lxb_css_at_rule_type_t type, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_at_rule_convert_to_undef(lxb_css_parser_t *parser, + lxb_css_rule_at_t *at); + +LXB_API lxb_status_t +lxb_css_at_rule_serialize(const void *style, lxb_css_at_rule_type_t type, + lexbor_serialize_cb_f cb, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_serialize_str(const void *style, lxb_css_at_rule_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str); +LXB_API lxb_status_t +lxb_css_at_rule_serialize_name(const void *style, lxb_css_at_rule_type_t type, + lexbor_serialize_cb_f cb, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_serialize_name_str(const void *style, lxb_css_at_rule_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str); + +/* _undef. */ + +LXB_API void * +lxb_css_at_rule__undef_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_at_rule__undef_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_at_rule__undef_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__undef_serialize_name(const void *at, lexbor_serialize_cb_f cb, + void *ctx); + +/* _custom. */ + +LXB_API void * +lxb_css_at_rule__custom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_at_rule__custom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_at_rule__custom_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__custom_serialize_name(const void *at, lexbor_serialize_cb_f cb, + void *ctx); + +/* Media. */ + +LXB_API void * +lxb_css_at_rule_media_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_at_rule_media_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_at_rule_media_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx); + +/* Namespace. */ + +LXB_API void * +lxb_css_at_rule_namespace_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_at_rule_namespace_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_at_rule_namespace_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx); + +/* Font-face. */ + +LXB_API void * +lxb_css_at_rule_font_face_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_at_rule_font_face_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_at_rule_font_face_serialize(const void *style, lexbor_serialize_cb_f cb, + void *ctx); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_AT_RULE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/const.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/const.h new file mode 100644 index 0000000..29ba3dd --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/const.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_AT_RULE_CONST_H +#define LXB_CSS_AT_RULE_CONST_H + + +#include "lexbor/css/value/const.h" + + +enum { + LXB_CSS_AT_RULE__UNDEF = 0x0000, + LXB_CSS_AT_RULE__CUSTOM = 0x0001, + LXB_CSS_AT_RULE_FONT_FACE = 0x0002, + LXB_CSS_AT_RULE_MEDIA = 0x0003, + LXB_CSS_AT_RULE_NAMESPACE = 0x0004, + LXB_CSS_AT_RULE__LAST_ENTRY = 0x0005 +}; +typedef uintptr_t lxb_css_at_rule_type_t; + + +#endif /* LXB_CSS_AT_RULE_CONST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/res.h new file mode 100644 index 0000000..6604fb9 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/res.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_AT_RULE_RES_H +#define LXB_CSS_AT_RULE_RES_H + +#include "lexbor/core/shs.h" +#include "lexbor/css/at_rule/const.h" + + +static const lxb_css_entry_at_rule_data_t lxb_css_at_rule_data[LXB_CSS_AT_RULE__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_AT_RULE__UNDEF, &lxb_css_at_rule__undef_cb, + lxb_css_at_rule__undef_create, lxb_css_at_rule__undef_destroy, lxb_css_at_rule__undef_serialize, (void *) (uintptr_t) LXB_CSS_AT_RULE__UNDEF}, + {(lxb_char_t *) "#сustom", 7, LXB_CSS_AT_RULE__CUSTOM, &lxb_css_at_rule__custom_cb, + lxb_css_at_rule__custom_create, lxb_css_at_rule__custom_destroy, lxb_css_at_rule__custom_serialize, (void *) (uintptr_t) LXB_CSS_AT_RULE__CUSTOM}, + {(lxb_char_t *) "font-face", 9, LXB_CSS_AT_RULE_FONT_FACE, &lxb_css_at_rule_font_face_cb, + lxb_css_at_rule_font_face_create, lxb_css_at_rule_font_face_destroy, lxb_css_at_rule_font_face_serialize, + NULL}, + {(lxb_char_t *) "media", 5, LXB_CSS_AT_RULE_MEDIA, &lxb_css_at_rule_media_cb, + lxb_css_at_rule_media_create, lxb_css_at_rule_media_destroy, lxb_css_at_rule_media_serialize, + NULL}, + {(lxb_char_t *) "namespace", 9, LXB_CSS_AT_RULE_NAMESPACE, &lxb_css_at_rule_namespace_cb, + lxb_css_at_rule_namespace_create, lxb_css_at_rule_namespace_destroy, lxb_css_at_rule_namespace_serialize, + NULL} +}; + +static const lexbor_shs_entry_t lxb_css_at_rule_shs[6] = +{ + {NULL, NULL, 5, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"media", (void *) &lxb_css_at_rule_data[LXB_CSS_AT_RULE_MEDIA], 5, 0}, + {"font-face", (void *) &lxb_css_at_rule_data[LXB_CSS_AT_RULE_FONT_FACE], 9, 0}, + {"namespace", (void *) &lxb_css_at_rule_data[LXB_CSS_AT_RULE_NAMESPACE], 9, 0} +}; + + +#endif /* LXB_CSS_AT_RULE_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.c new file mode 100644 index 0000000..4df2f60 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.c @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/at_rule.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/rule.h" +#include "lexbor/css/blank.h" +#include "lexbor/css/at_rule/state.h" + + +/* Undef */ + +bool +lxb_css_at_rule__undef_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_begin = token->offset; + + /* Skip all prelude. */ + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_at_rule__undef_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + + at->prelude_end = token->offset; + + status = lxb_css_make_data(parser, &at->u.undef->prelude, + at->prelude_begin, at->prelude_end); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail_status(parser); + } + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_at_rule__undef_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return lxb_css_state_at_rule_block_begin(parser, token, ctx, out_rule); +} + +bool +lxb_css_at_rule__undef_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + /* For _UNDEF and _CUSTOM, access here is not possible. */ + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_at_rule__undef_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + lxb_css_rule_list_t *block = lxb_css_syntax_returned(parser); + + /* + * Here we do not check for failed because it cannot happen; we do not call + * failed anywhere in _UNDEF and _CUSTOM, we simply skip the tokens. + */ + + at->u.undef->block = block; + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} + +/* Custom */ + +bool +lxb_css_at_rule__custom_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_at_rule__undef_prelude(parser, token, ctx); +} + +lxb_status_t +lxb_css_at_rule__custom_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + + at->prelude_end = token->offset; + + status = lxb_css_make_data(parser, &at->u.custom->prelude, + at->prelude_begin, at->prelude_end); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail_status(parser); + } + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_at_rule__custom_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return lxb_css_state_at_rule_block_begin(parser, token, ctx, out_rule); +} + +bool +lxb_css_at_rule__custom_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_at_rule__undef_prelude_failed(parser, token, ctx); +} + +lxb_status_t +lxb_css_at_rule__custom_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + lxb_css_rule_list_t *block = lxb_css_syntax_returned(parser); + + /* + * Here we do not check for failed because it cannot happen; we do not call + * failed anywhere in _UNDEF and _CUSTOM, we simply skip the tokens. + */ + + at->u.custom->block = block; + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} + +/* Namespace */ + +bool +lxb_css_at_rule_namespace_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_begin = token->offset; + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_at_rule_namespace_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_end = token->offset; + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_namespace_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lxb_css_parser_failed_set(parser, true); + + return lxb_css_state_at_rule_block_begin(parser, token, ctx, out_rule); +} + +bool +lxb_css_at_rule_namespace_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_at_rule__undef_prelude_failed(parser, token, ctx); +} + +lxb_status_t +lxb_css_at_rule_namespace_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + + if (failed) { + status = lxb_css_at_rule_convert_to_undef(parser, at); + if (status != LXB_STATUS_OK) { + return status; + } + + at->u.undef->block = lxb_css_syntax_returned(parser); + } + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} + +/* Media */ + +bool +lxb_css_at_rule_media_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_begin = token->offset; + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_at_rule_media_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_end = token->offset; + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_media_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return lxb_css_state_at_rule_block_begin(parser, token, ctx, out_rule); +} + +bool +lxb_css_at_rule_media_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_at_rule__undef_prelude_failed(parser, token, ctx); +} + +lxb_status_t +lxb_css_at_rule_media_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + lxb_css_rule_list_t *list = lxb_css_syntax_returned(parser); + + if (list != NULL && !failed) { + at->u.media->block = list; + } + else { + status = lxb_css_at_rule_convert_to_undef(parser, at); + if (status != LXB_STATUS_OK) { + return status; + } + + at->u.undef->block = list; + } + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} + +/* Font-face */ + +bool +lxb_css_at_rule_font_face_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_begin = token->offset; + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_at_rule_font_face_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_end = token->offset; + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_font_face_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return lxb_css_state_at_rule_block_begin(parser, token, ctx, out_rule); +} + +bool +lxb_css_at_rule_font_face_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_at_rule__undef_prelude_failed(parser, token, ctx); +} + +lxb_status_t +lxb_css_at_rule_font_face_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + lxb_css_rule_list_t *block = lxb_css_syntax_returned(parser); + + if (!failed) { + at->u.font_face->block = block; + } + else { + status = lxb_css_at_rule_convert_to_undef(parser, at); + if (status != LXB_STATUS_OK) { + return status; + } + + at->u.undef->block = block; + } + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.h new file mode 100644 index 0000000..337a23c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/state.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_AT_RULE_STATE_H +#define LXB_CSS_AT_RULE_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" + + +LXB_API bool +lxb_css_at_rule__undef_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__undef_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_at_rule__undef_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +LXB_API bool +lxb_css_at_rule__undef_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__undef_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API bool +lxb_css_at_rule__custom_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__custom_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_at_rule__custom_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +LXB_API bool +lxb_css_at_rule__custom_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule__custom_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API bool +lxb_css_at_rule_namespace_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_namespace_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_namespace_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +LXB_API bool +lxb_css_at_rule_namespace_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_namespace_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API bool +lxb_css_at_rule_media_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_media_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_media_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +LXB_API bool +lxb_css_at_rule_media_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_media_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API bool +lxb_css_at_rule_font_face_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_font_face_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_at_rule_font_face_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +LXB_API bool +lxb_css_at_rule_font_face_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +LXB_API lxb_status_t +lxb_css_at_rule_font_face_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_AT_RULE_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/types.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/types.h new file mode 100644 index 0000000..1079bb6 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/at_rule/types.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_AT_RULE_TYPES_H +#define LXB_CSS_AT_RULE_TYPES_H + + +static const lxb_css_syntax_cb_at_rule_t lxb_css_at_rule__undef_cb = { + .prelude = lxb_css_at_rule__undef_prelude, + .prelude_end = lxb_css_at_rule__undef_prelude_end, + .block = lxb_css_at_rule__undef_block, + .cb.failed = lxb_css_at_rule__undef_prelude_failed, + .cb.end = lxb_css_at_rule__undef_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_at_rule__custom_cb = { + .prelude = lxb_css_at_rule__custom_prelude, + .prelude_end = lxb_css_at_rule__custom_prelude_end, + .block = lxb_css_at_rule__custom_block, + .cb.failed = lxb_css_at_rule__custom_prelude_failed, + .cb.end = lxb_css_at_rule__custom_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_at_rule_font_face_cb = { + .prelude = lxb_css_at_rule_font_face_prelude, + .prelude_end = lxb_css_at_rule_font_face_prelude_end, + .block = lxb_css_at_rule_font_face_block, + .cb.failed = lxb_css_at_rule_font_face_prelude_failed, + .cb.end = lxb_css_at_rule_font_face_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_at_rule_media_cb = { + .prelude = lxb_css_at_rule_media_prelude, + .prelude_end = lxb_css_at_rule_media_prelude_end, + .block = lxb_css_at_rule_media_block, + .cb.failed = lxb_css_at_rule_media_prelude_failed, + .cb.end = lxb_css_at_rule_media_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_at_rule_namespace_cb = { + .prelude = lxb_css_at_rule_namespace_prelude, + .prelude_end = lxb_css_at_rule_namespace_prelude_end, + .block = lxb_css_at_rule_namespace_block, + .cb.failed = lxb_css_at_rule_namespace_prelude_failed, + .cb.end = lxb_css_at_rule_namespace_end +}; + + + +#endif /* LXB_CSS_AT_RULE_TYPES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/base.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/base.h new file mode 100644 index 0000000..26712ce --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/base.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2019-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_BASE_H +#define LEXBOR_CSS_BASE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/core/base.h" +#include "lexbor/core/mraw.h" +#include "lexbor/core/str.h" + + +#define LXB_CSS_VERSION_MAJOR 1 +#define LXB_CSS_VERSION_MINOR 4 +#define LXB_CSS_VERSION_PATCH 0 + +#define LXB_CSS_VERSION_STRING \ + LEXBOR_STRINGIZE(LXB_CSS_VERSION_MAJOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_VERSION_MINOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_VERSION_PATCH) + + +typedef struct lxb_css_memory { + lexbor_dobject_t *objs; + lexbor_mraw_t *mraw; + lexbor_mraw_t *tree; + + size_t ref_count; +} +lxb_css_memory_t; + +typedef uint32_t lxb_css_type_t; + +typedef struct lxb_css_parser lxb_css_parser_t; +typedef struct lxb_css_parser_state lxb_css_parser_state_t; +typedef struct lxb_css_parser_error lxb_css_parser_error_t; + +typedef struct lxb_css_syntax_tokenizer lxb_css_syntax_tokenizer_t; +typedef struct lxb_css_syntax_token lxb_css_syntax_token_t; + +/* Callbacks. */ + +typedef bool +(*lxb_css_parser_state_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +typedef void * +(*lxb_css_style_create_f)(lxb_css_memory_t *memory); + +typedef lxb_status_t +(*lxb_css_style_serialize_f)(const void *style, lexbor_serialize_cb_f cb, + void *ctx); + +typedef void * +(*lxb_css_style_destroy_f)(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +/* StyleSheet tree structures. */ + +typedef struct lxb_css_stylesheet lxb_css_stylesheet_t; +typedef struct lxb_css_rule_list lxb_css_rule_list_t; +typedef struct lxb_css_rule_style lxb_css_rule_style_t; +typedef struct lxb_css_rule_bad_style lxb_css_rule_bad_style_t; +typedef struct lxb_css_rule_declaration_list lxb_css_rule_declaration_list_t; +typedef struct lxb_css_rule_declaration lxb_css_rule_declaration_t; +typedef struct lxb_css_rule_at lxb_css_rule_at_t; + +typedef struct { + lxb_char_t *name; + size_t length; + uintptr_t unique; + lxb_css_parser_state_f state; + lxb_css_style_create_f create; + lxb_css_style_destroy_f destroy; + lxb_css_style_serialize_f serialize; + void *initial; +} +lxb_css_entry_data_t; + +typedef struct { + lxb_char_t *name; + size_t length; + uintptr_t unique; + + /* const lxb_css_syntax_cb_at_rule_t */ + const void *cbs; + + lxb_css_style_create_f create; + lxb_css_style_destroy_f destroy; + lxb_css_style_serialize_f serialize; + void *initial; +} +lxb_css_entry_at_rule_data_t; + +typedef struct { + lxb_char_t *name; + size_t length; + uintptr_t unique; +} +lxb_css_data_t; + + +LXB_API lxb_css_memory_t * +lxb_css_memory_create(void); + +LXB_API lxb_status_t +lxb_css_memory_init(lxb_css_memory_t *memory, size_t prepare_count); + +LXB_API void +lxb_css_memory_clean(lxb_css_memory_t *memory); + +LXB_API lxb_css_memory_t * +lxb_css_memory_destroy(lxb_css_memory_t *memory, bool self_destroy); + +LXB_API lxb_css_memory_t * +lxb_css_memory_ref_inc(lxb_css_memory_t *memory); + +LXB_API void +lxb_css_memory_ref_dec(lxb_css_memory_t *memory); + +LXB_API lxb_css_memory_t * +lxb_css_memory_ref_dec_destroy(lxb_css_memory_t *memory); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_BASE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.c new file mode 100644 index 0000000..80e60f7 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.c @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/blank.h" +#include "lexbor/css/state.h" +#include "lexbor/css/css.h" +#include "lexbor/css/at_rule/state.h" + + +static bool +lxb_css_blank_list_rules_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_blank_list_rules_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_at_rule_t * +lxb_css_blank_at_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static bool +lxb_css_blank_at_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_blank_at_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_block_t * +lxb_css_blank_at_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +static bool +lxb_css_blank_at_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t +lxb_css_blank_at_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_blank_qualified_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static bool +lxb_css_blank_qualified_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_blank_qualified_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_block_t * +lxb_css_blank_qualified_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static bool +lxb_css_blank_qualified_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t +lxb_css_blank_qualified_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static bool +lxb_css_blank_block_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_blank_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_declarations_t * +lxb_css_blank_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static lxb_css_parser_state_f +lxb_css_blank_declaration_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +static bool +lxb_css_blank_declaration_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_blank_declaration_end(lxb_css_parser_t *parser, + void *declarations, void *ctx, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed); + +static lxb_status_t +lxb_css_blank_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static bool +lxb_css_blank_declarations_bad(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +static const lxb_css_syntax_cb_list_rules_t lxb_css_blank_list_rules = { + .at_rule = lxb_css_blank_at_rule_begin, + .qualified_rule = lxb_css_blank_qualified_rule_begin, + .next = lxb_css_blank_list_rules_next, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_blank_list_rules_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_blank_at_rule = { + .prelude = lxb_css_blank_at_rule_prelude, + .prelude_end = lxb_css_blank_at_rule_prelude_end, + .block = lxb_css_blank_at_rule_block_begin, + .cb.failed = lxb_css_blank_at_rule_prelude_failed, + .cb.end = lxb_css_blank_at_rule_end +}; + +static const lxb_css_syntax_cb_qualified_rule_t lxb_css_blank_qualified_rule = { + .prelude = lxb_css_blank_qualified_rule_prelude, + .prelude_end = lxb_css_blank_qualified_rule_prelude_end, + .block = lxb_css_blank_qualified_rule_block_begin, + .cb.failed = lxb_css_blank_qualified_rule_prelude_failed, + .cb.end = lxb_css_blank_qualified_rule_end +}; + +static const lxb_css_syntax_cb_block_t lxb_css_blank_block = { + .at_rule = lxb_css_blank_at_rule_begin, + .declarations = lxb_css_blank_declarations_begin, + .qualified_rule = lxb_css_blank_qualified_rule_begin, + .next = lxb_css_blank_block_next, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_blank_block_end, +}; + +static const lxb_css_syntax_cb_declarations_t lxb_css_blank_declaration = { + .name = lxb_css_blank_declaration_name, + .end = lxb_css_blank_declaration_end, + .cb.failed = lxb_css_blank_declarations_bad, + .cb.end = lxb_css_blank_declarations_end +}; + + +const lxb_css_syntax_cb_list_rules_t * +lxb_css_blank_cb_list_rules(void) +{ + return &lxb_css_blank_list_rules; +} + +const lxb_css_syntax_cb_at_rule_t * +lxb_css_blank_cb_at_rule(void) +{ + return &lxb_css_blank_at_rule; +} + +const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_blank_cb_qualified_rule(void) +{ + return &lxb_css_blank_qualified_rule; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_blank_cb_block(void) +{ + return &lxb_css_blank_block; +} + +const lxb_css_syntax_cb_declarations_t * +lxb_css_blank_cb_declarations(void) +{ + return &lxb_css_blank_declaration; +} + +static bool +lxb_css_blank_list_rules_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_list_rules_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_at_rule_t * +lxb_css_blank_at_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule) +{ + return &lxb_css_blank_at_rule; +} + +static bool +lxb_css_blank_at_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_at_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_block_t * +lxb_css_blank_at_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return &lxb_css_blank_block; +} + +static bool +lxb_css_blank_at_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_at_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_blank_qualified_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return &lxb_css_blank_qualified_rule; +} + +static bool +lxb_css_blank_qualified_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_qualified_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_block_t * +lxb_css_blank_qualified_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return &lxb_css_blank_block; +} + +static bool +lxb_css_blank_qualified_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_qualified_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static bool +lxb_css_blank_block_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_declarations_t * +lxb_css_blank_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return &lxb_css_blank_declaration; +} + +static lxb_css_parser_state_f +lxb_css_blank_declaration_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + return lxb_css_blank_declaration_value; +} + +static bool +lxb_css_blank_declaration_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_blank_declaration_end(lxb_css_parser_t *parser, + void *declarations, void *ctx, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed) +{ + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_blank_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static bool +lxb_css_blank_declarations_bad(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.h new file mode 100644 index 0000000..05a13bf --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/blank.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_BLANK_H +#define LXB_CSS_BLANK_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/syntax/syntax.h" + + +LXB_API const lxb_css_syntax_cb_list_rules_t * +lxb_css_blank_cb_list_rules(void); + +LXB_API const lxb_css_syntax_cb_at_rule_t * +lxb_css_blank_cb_at_rule(void); + +LXB_API const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_blank_cb_qualified_rule(void); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_blank_cb_block(void); + +LXB_API const lxb_css_syntax_cb_declarations_t * +lxb_css_blank_cb_declarations(void); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_BLANK_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.c new file mode 100644 index 0000000..68c6c97 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" + + +typedef struct { + lexbor_str_t *str; + lexbor_mraw_t *mraw; +} +lxb_css_str_ctx_t; + + +static lxb_status_t +lxb_css_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx); + + +lxb_css_memory_t * +lxb_css_memory_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_memory_t)); +} + +lxb_status_t +lxb_css_memory_init(lxb_css_memory_t *memory, size_t prepare_count) +{ + lxb_status_t status; + + static const size_t size_mem = lexbor_max(sizeof(lxb_css_selector_t), + sizeof(lxb_css_selector_list_t)); + + if (memory == NULL) { + return LXB_STATUS_ERROR_INCOMPLETE_OBJECT; + } + + if (prepare_count < 64) { + prepare_count = 64; + } + + if (memory->objs == NULL) { + memory->objs = lexbor_dobject_create(); + status = lexbor_dobject_init(memory->objs, prepare_count, size_mem); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + if (memory->tree == NULL) { + prepare_count = prepare_count * 96; + + memory->tree = lexbor_mraw_create(); + status = lexbor_mraw_init(memory->tree, prepare_count); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + if (memory->mraw == NULL) { + memory->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(memory->mraw, 4096); + if (status != LXB_STATUS_OK) { + goto failed; + } + } + + memory->ref_count = 1; + + return LXB_STATUS_OK; + +failed: + + (void) lxb_css_memory_destroy(memory, false); + + return status; +} + +void +lxb_css_memory_clean(lxb_css_memory_t *memory) +{ + if (memory->objs != NULL) { + lexbor_dobject_clean(memory->objs); + } + + if (memory->mraw != NULL) { + lexbor_mraw_clean(memory->mraw); + } + + if (memory->tree != NULL) { + lexbor_mraw_clean(memory->tree); + } +} + +lxb_css_memory_t * +lxb_css_memory_destroy(lxb_css_memory_t *memory, bool self_destroy) +{ + if (memory == NULL) { + return NULL; + } + + if (memory->objs != NULL) { + memory->objs = lexbor_dobject_destroy(memory->objs, true); + } + + if (memory->mraw != NULL) { + memory->mraw = lexbor_mraw_destroy(memory->mraw, true); + } + + if (memory->tree != NULL) { + memory->tree = lexbor_mraw_destroy(memory->tree, true); + } + + if (self_destroy) { + return lexbor_free(memory); + } + + return memory; +} + +lxb_css_memory_t * +lxb_css_memory_ref_inc(lxb_css_memory_t *memory) +{ + if (SIZE_MAX - memory->ref_count == 0) { + return NULL; + } + + memory->ref_count++; + + return memory; +} + +void +lxb_css_memory_ref_dec(lxb_css_memory_t *memory) +{ + if (memory->ref_count > 0) { + memory->ref_count--; + } +} + +lxb_css_memory_t * +lxb_css_memory_ref_dec_destroy(lxb_css_memory_t *memory) +{ + if (memory->ref_count > 0) { + memory->ref_count--; + } + + if (memory->ref_count == 0) { + return lxb_css_memory_destroy(memory, true); + } + + return memory; +} + +lxb_status_t +lxb_css_make_data(lxb_css_parser_t *parser, lexbor_str_t *str, + size_t begin, size_t end) +{ + size_t length; + const lxb_char_t *p; + + p = parser->tkz->in_begin; + length = end - begin; + + if (str->data == NULL) { + (void) lexbor_str_init(str, parser->memory->mraw, length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + memcpy(str->data, p + begin, length); + + str->length = length; + str->data[str->length] = '\0'; + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_serialize_char_handler(const void *style, lxb_css_style_serialize_f cb, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = cb(style, lexbor_serialize_length_cb, &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = cb(style, lexbor_serialize_copy_cb, &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_serialize_str_handler(const void *style, lexbor_str_t *str, + lexbor_mraw_t *mraw, + lxb_css_style_serialize_f cb) +{ + lxb_css_str_ctx_t ctx; + + ctx.str = str; + ctx.mraw = mraw; + + if (str->data == NULL) { + lexbor_str_init(str, mraw, 1); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + return cb(style, lxb_css_str_cb, &ctx); +} + +static lxb_status_t +lxb_css_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx) +{ + lxb_char_t *ptr; + lxb_css_str_ctx_t *ctx = (lxb_css_str_ctx_t *) cb_ctx; + + ptr = lexbor_str_append(ctx->str, ctx->mraw, data, len); + + return (ptr != NULL) ? LXB_STATUS_OK : LXB_STATUS_ERROR_MEMORY_ALLOCATION; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.h new file mode 100644 index 0000000..4ba7152 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/css.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_H +#define LXB_CSS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/log.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/stylesheet.h" +#include "lexbor/css/property.h" +#include "lexbor/css/value.h" +#include "lexbor/css/at_rule.h" +#include "lexbor/css/rule.h" +#include "lexbor/css/unit.h" +#include "lexbor/css/state.h" +#include "lexbor/css/declaration.h" +#include "lexbor/css/syntax/tokenizer/error.h" +#include "lexbor/css/syntax/tokenizer.h" +#include "lexbor/css/syntax/token.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/syntax/anb.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/selector.h" +#include "lexbor/css/selectors/state.h" +#include "lexbor/css/selectors/pseudo.h" + + +LXB_API lxb_status_t +lxb_css_make_data(lxb_css_parser_t *parser, lexbor_str_t *str, + size_t begin, size_t end); + +LXB_API lxb_char_t * +lxb_css_serialize_char_handler(const void *style, lxb_css_style_serialize_f cb, + size_t *out_length); +LXB_API lxb_status_t +lxb_css_serialize_str_handler(const void *style, lexbor_str_t *str, + lexbor_mraw_t *mraw, lxb_css_style_serialize_f cb); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.c new file mode 100644 index 0000000..e275714 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.c @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/declaration.h" + + +lxb_css_rule_declaration_t * +lxb_css_declaration_create(lxb_css_parser_t *parser, + const lxb_char_t *name, size_t length, + const lxb_css_entry_data_t **out_entry) +{ + void *prop; + const lxb_css_entry_data_t *entry; + lxb_css_property__custom_t *custom; + lxb_css_rule_declaration_t *declar; + + declar = lxb_css_rule_declaration_create(parser->memory); + if (declar == NULL) { + return NULL; + } + + entry = lxb_css_property_by_name(name, length); + if (entry == NULL) { + entry = lxb_css_property_by_id(LXB_CSS_PROPERTY__CUSTOM); + + prop = entry->create(parser->memory); + if (prop == NULL) { + goto failed; + } + + custom = prop; + + (void) lexbor_str_init(&custom->name, parser->memory->mraw, length); + if (custom->name.data == NULL) { + goto failed; + } + + memcpy(custom->name.data, name, length); + + custom->name.length = length; + custom->name.data[custom->name.length] = 0x00; + } + else { + prop = entry->create(parser->memory); + if (prop == NULL) { + goto failed; + } + } + + declar->type = entry->unique; + declar->u.user = prop; + + if (out_entry != NULL) { + *out_entry = entry; + } + + return declar; + +failed: + + if (prop != NULL) { + (void) entry->destroy(parser->memory, prop, true); + } + + if (declar != NULL) { + (void) lxb_css_rule_declaration_destroy(declar, true); + } + + if (out_entry != NULL) { + *out_entry = NULL; + } + + return NULL; +} + +lxb_css_rule_declaration_list_t * +lxb_css_declaration_list_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_syntax_parse_declarations(parser, + lxb_css_state_cb_declarations(), + data, length); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.h new file mode 100644 index 0000000..53ab061 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/declaration.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_DECLARATION_H +#define LXB_CSS_DECLARATION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/css.h" +#include "lexbor/core/mraw.h" + + +LXB_API lxb_css_rule_declaration_t * +lxb_css_declaration_create(lxb_css_parser_t *parser, + const lxb_char_t *name, size_t length, + const lxb_css_entry_data_t **out_entry); + + +LXB_API lxb_css_rule_declaration_list_t * +lxb_css_declaration_list_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_DECLARATION_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.c new file mode 100644 index 0000000..47895ed --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/log.h" + + +typedef struct { + const char *msg; + size_t length; +} +lxb_css_log_type_str_t; + + +static const lxb_css_log_type_str_t lxb_css_log_types_map[] = { + {"Info", 4}, + {"Warning", 7}, + {"Error", 5}, + {"Syntax error", 12} +}; + + +lxb_css_log_t * +lxb_css_log_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_log_t)); +} + +lxb_status_t +lxb_css_log_init(lxb_css_log_t *log, lexbor_mraw_t *mraw) +{ + lxb_status_t status; + + if (log == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + status = lexbor_array_obj_init(&log->messages, 64, + sizeof(lxb_css_log_message_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + if (mraw != NULL) { + log->mraw = mraw; + log->self_mraw = false; + return LXB_STATUS_OK; + } + + log->self_mraw = true; + + log->mraw = lexbor_mraw_create(); + + return lexbor_mraw_init(log->mraw, 4096); +} + +void +lxb_css_log_clean(lxb_css_log_t *log) +{ + if (log != NULL) { + lexbor_array_obj_clean(&log->messages); + + if (log->self_mraw) { + lexbor_mraw_clean(log->mraw); + } + } +} + +lxb_css_log_t * +lxb_css_log_destroy(lxb_css_log_t *log, bool self_destroy) +{ + if (log == NULL) { + return NULL; + } + + (void) lexbor_array_obj_destroy(&log->messages, false); + + if (log->self_mraw) { + (void) lexbor_mraw_destroy(log->mraw, true); + } + + if (self_destroy) { + log = lexbor_free(log); + } + + return log; +} + +lxb_css_log_message_t * +lxb_css_log_append(lxb_css_log_t *log, lxb_css_log_type_t type, + const lxb_char_t *str, size_t length) +{ + lxb_css_log_message_t *msg; + + msg = lexbor_array_obj_push(&log->messages); + if (msg == NULL) { + return NULL; + } + + if (lexbor_str_init(&msg->text, log->mraw, length) == NULL) { + lexbor_array_obj_pop(&log->messages); + return NULL; + } + + memcpy(msg->text.data, str, length); + msg->text.length = length; + + msg->text.data[length] = '\0'; + + msg->type = type; + + return msg; +} + +lxb_css_log_message_t * +lxb_css_log_push(lxb_css_log_t *log, lxb_css_log_type_t type, size_t length) +{ + lxb_css_log_message_t *msg; + + msg = lexbor_array_obj_push(&log->messages); + if (msg == NULL) { + return NULL; + } + + if (lexbor_str_init(&msg->text, log->mraw, length) == NULL) { + lexbor_array_obj_pop(&log->messages); + return NULL; + } + + msg->type = type; + + return msg; +} + +lxb_css_log_message_t * +lxb_css_log_format(lxb_css_log_t *log, lxb_css_log_type_t type, + const char *format, ...) +{ + (void) type; (void) format; + + /* Size patch: message text is never read by the shim (parser->status is + * the only observable), so arguments are not formatted. The record itself + * still exists: callers treat NULL as an allocation failure. */ + return lxb_css_log_push(log, type, 0); +} + +lxb_css_log_message_t * +lxb_css_log_not_supported(lxb_css_log_t *log, + const char *module_name, const char *description) +{ + (void) module_name; (void) description; + + return lxb_css_log_push(log, LXB_CSS_LOG_SYNTAX_ERROR, 0); +} + +const lxb_char_t * +lxb_css_log_type_by_id(lxb_css_log_type_t type, size_t *out_length) +{ + if (out_length != NULL) { + *out_length = lxb_css_log_types_map[type].length; + } + + return (const lxb_char_t *) lxb_css_log_types_map[type].msg; +} + + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.h new file mode 100644 index 0000000..0c03f46 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/log.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_LOG_H +#define LEXBOR_CSS_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/mraw.h" +#include "lexbor/core/str.h" +#include "lexbor/core/array_obj.h" +#include "lexbor/css/base.h" + + +typedef enum { + LXB_CSS_LOG_INFO = 0, + LXB_CSS_LOG_WARNING, + LXB_CSS_LOG_ERROR, + LXB_CSS_LOG_SYNTAX_ERROR +} +lxb_css_log_type_t; + +typedef struct { + lexbor_str_t text; + lxb_css_log_type_t type; +} +lxb_css_log_message_t; + +typedef struct { + lexbor_array_obj_t messages; + lexbor_mraw_t *mraw; + bool self_mraw; +} +lxb_css_log_t; + + +LXB_API lxb_css_log_t * +lxb_css_log_create(void); + +LXB_API lxb_status_t +lxb_css_log_init(lxb_css_log_t *log, lexbor_mraw_t *mraw); + +LXB_API void +lxb_css_log_clean(lxb_css_log_t *log); + +LXB_API lxb_css_log_t * +lxb_css_log_destroy(lxb_css_log_t *log, bool self_destroy); + +LXB_API lxb_css_log_message_t * +lxb_css_log_append(lxb_css_log_t *log, lxb_css_log_type_t type, + const lxb_char_t *str, size_t length); + +LXB_API lxb_css_log_message_t * +lxb_css_log_push(lxb_css_log_t *log, lxb_css_log_type_t type, size_t length); + +LXB_API lxb_css_log_message_t * +lxb_css_log_format(lxb_css_log_t *log, lxb_css_log_type_t type, + const char *format, ...); + +LXB_API lxb_css_log_message_t * +lxb_css_log_not_supported(lxb_css_log_t *log, + const char *module_name, const char *description); + +LXB_API const lxb_char_t * +lxb_css_log_type_by_id(lxb_css_log_type_t type, size_t *out_length); + +LXB_API lxb_status_t +lxb_css_log_serialize(lxb_css_log_t *log, lexbor_serialize_cb_f cb, void *ctx, + const lxb_char_t *indent, size_t indent_length); + +LXB_API lxb_char_t * +lxb_css_log_serialize_char(lxb_css_log_t *log, size_t *out_length, + const lxb_char_t *indent, size_t indent_length); + +LXB_API lxb_status_t +lxb_css_log_message_serialize(lxb_css_log_message_t *msg, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_char_t * +lxb_css_log_message_serialize_char(lxb_css_log_message_t *msg, + size_t *out_length); + + +/* + * Inline functions + */ +lxb_inline size_t +lxb_css_log_length(lxb_css_log_t *log) +{ + return lexbor_array_obj_length(&log->messages); +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_LOG_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.c new file mode 100644 index 0000000..6251438 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.c @@ -0,0 +1,563 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/parser.h" +#include "lexbor/css/state.h" +#include "lexbor/css/syntax/syntax.h" + + +lxb_css_parser_t * +lxb_css_parser_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_parser_t)); +} + +lxb_status_t +lxb_css_parser_init(lxb_css_parser_t *parser, lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + static const size_t lxb_rules_length = 128; + static const size_t lxb_states_length = 1024; + + if (parser == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + /* Stack */ + parser->states_begin = lexbor_malloc(sizeof(lxb_css_parser_state_t) + * lxb_states_length); + if (parser->states_begin == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->states = parser->states_begin; + parser->states_end = parser->states_begin + lxb_states_length; + + memset(parser->states, 0x00, sizeof(lxb_css_parser_state_t)); + parser->states->root = true; + + /* Syntax */ + parser->my_tkz = false; + + if (tkz == NULL) { + tkz = lxb_css_syntax_tokenizer_create(); + status = lxb_css_syntax_tokenizer_init(tkz); + if (status != LXB_STATUS_OK) { + return status; + } + + parser->my_tkz = true; + } + + /* Rules */ + parser->rules_begin = lexbor_malloc(sizeof(lxb_css_syntax_rule_t) + * lxb_rules_length); + if (parser->rules_begin == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->rules_end = parser->rules_begin + lxb_rules_length; + parser->rules = parser->rules_begin; + + /* + * Zero those parameters that can be used (passed to the function). + * The parser->rules->phase parameter will be assigned at the end of the + * parsing. + * + * The point is that parser->rules[0] is used as a stub before exiting + * parsing. + */ + parser->rules->context = NULL; + + /* Temp */ + parser->str.length = 0; + parser->str_size = 4096; + + parser->str.data = lexbor_malloc(sizeof(lxb_char_t) * parser->str_size); + if (parser->str.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->log = lxb_css_log_create(); + status = lxb_css_log_init(parser->log, NULL); + if (status != LXB_STATUS_OK) { + return status; + } + + parser->tkz = tkz; + parser->types_begin = NULL; + parser->types_pos = NULL; + parser->types_end = NULL; + parser->stage = LXB_CSS_PARSER_CLEAN; + parser->status = LXB_STATUS_OK; + parser->fake_null = false; + parser->token_end.type = LXB_CSS_SYNTAX_TOKEN__END; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_parser_selectors_init(lxb_css_parser_t *parser) +{ + lxb_status_t status; + + parser->selectors = lxb_css_selectors_create(); + status = lxb_css_selectors_init(parser->selectors); + if (status != LXB_STATUS_OK) { + parser->selectors = lxb_css_selectors_destroy(parser->selectors, true); + } + + return status; +} + +void +lxb_css_parser_selectors_destroy(lxb_css_parser_t *parser) +{ + if (parser->selectors != NULL) { + parser->selectors = lxb_css_selectors_destroy(parser->selectors, true); + } +} + +void +lxb_css_parser_clean(lxb_css_parser_t *parser) +{ + lxb_css_syntax_tokenizer_clean(parser->tkz); + lxb_css_log_clean(parser->log); + + parser->rules = parser->rules_begin; + parser->states = parser->states_begin; + parser->types_pos = parser->types_begin; + parser->stage = LXB_CSS_PARSER_CLEAN; + parser->status = LXB_STATUS_OK; + parser->str.length = 0; + parser->fake_null = false; +} + +void +lxb_css_parser_erase(lxb_css_parser_t *parser) +{ + lxb_css_parser_clean(parser); + + if (parser->memory != NULL) { + lxb_css_memory_clean(parser->memory); + } +} + +lxb_css_parser_t * +lxb_css_parser_destroy(lxb_css_parser_t *parser, bool self_destroy) +{ + if (parser == NULL) { + return NULL; + } + + if (parser->my_tkz) { + parser->tkz = lxb_css_syntax_tokenizer_destroy(parser->tkz); + } + + parser->log = lxb_css_log_destroy(parser->log, true); + + if (parser->rules_begin != NULL) { + parser->rules_begin = lexbor_free(parser->rules_begin); + } + + if (parser->states_begin != NULL) { + parser->states_begin = lexbor_free(parser->states_begin); + } + + if (parser->types_begin != NULL) { + parser->types_begin = lexbor_free(parser->types_begin); + } + + if (parser->str.data != NULL) { + parser->str.data = lexbor_free(parser->str.data); + } + + if (self_destroy) { + return lexbor_free(parser); + } + + return parser; +} + +lxb_css_parser_state_t * +lxb_css_parser_states_push(lxb_css_parser_t *parser, + lxb_css_parser_state_f state, void *ctx, bool root) +{ + size_t length, cur_length; + lxb_css_parser_state_t *states = ++parser->states; + + if (states >= parser->states_end) { + cur_length = states - parser->states_begin; + + if (SIZE_MAX - cur_length < 1024) { + goto memory_error; + } + + length = cur_length + 1024; + + states = lexbor_realloc(parser->states_begin, + length * sizeof(lxb_css_parser_state_t)); + if (states == NULL) { + goto memory_error; + } + + parser->states_begin = states; + parser->states_end = states + length; + parser->states = states + cur_length; + + states = parser->states; + } + + states->state = state; + states->context = ctx; + states->root = root; + + return states; + +memory_error: + + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + + return NULL; +} + +lxb_css_parser_state_t * +lxb_css_parser_states_next(lxb_css_parser_t *parser, + lxb_css_parser_state_f next, + lxb_css_parser_state_f back, void *ctx, bool root) +{ + lxb_css_parser_state_t *state; + + state = lxb_css_parser_states_push(parser, back, ctx, root); + if (state == NULL) { + return NULL; + } + + parser->rules->state = next; + + return state; +} + +lxb_status_t +lxb_css_parser_types_push(lxb_css_parser_t *parser, + lxb_css_syntax_token_type_t type) +{ + size_t length, new_length; + lxb_css_syntax_token_type_t *tmp; + + if (parser->types_pos >= parser->types_end) { + length = parser->types_end - parser->types_begin; + + if ((SIZE_MAX - length) < 1024) { + return LXB_STATUS_ERROR_OVERFLOW; + } + + new_length = length + 1024; + + tmp = lexbor_realloc(parser->types_begin, + new_length * sizeof(lxb_css_syntax_token_type_t)); + if (tmp == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->types_begin = tmp; + parser->types_end = tmp + new_length; + parser->types_pos = parser->types_begin + length; + } + + *parser->types_pos++ = type; + + return LXB_STATUS_OK; +} + +bool +lxb_css_parser_stop(lxb_css_parser_t *parser) +{ + parser->loop = false; + return true; +} + +bool +lxb_css_parser_fail(lxb_css_parser_t *parser, lxb_status_t status) +{ + parser->status = status; + parser->loop = false; + return true; +} + +bool +lxb_css_parser_unexpected(lxb_css_parser_t *parser) +{ + (void) lxb_css_parser_unexpected_status(parser); + return true; +} + +bool +lxb_css_parser_success(lxb_css_parser_t *parser) +{ + parser->rules->state = lxb_css_state_success; + return true; +} + +bool +lxb_css_parser_failed(lxb_css_parser_t *parser) +{ + lxb_css_syntax_rule_t *rule = parser->rules; + + rule->state = rule->cbx.cb->failed; + rule->failed = true; + + return true; +} + +lxb_status_t +lxb_css_parser_unexpected_status(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + parser->rules->failed = true; + + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +bool +lxb_css_parser_unexpected_data(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + static const char selectors[] = "Selectors"; + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + if (lxb_css_syntax_token_error(parser, token, selectors) == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return true; +} + +lxb_status_t +lxb_css_parser_unexpected_data_status(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + static const char selectors[] = "Selectors"; + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + if (lxb_css_syntax_token_error(parser, token, selectors) == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +void * +lxb_css_parser_memory_fail_null(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + parser->loop = false; + return NULL; +} + +bool +lxb_css_parser_memory_fail(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + parser->loop = false; + return true; +} + +lxb_status_t +lxb_css_parser_memory_fail_status(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + parser->loop = false; + + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; +} + +/* + * No-inline functions for ABI. + */ +lxb_status_t +lxb_css_parser_status_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_status(parser); +} + +lxb_css_memory_t * +lxb_css_parser_memory_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_memory(parser); +} + +void +lxb_css_parser_memory_set_noi(lxb_css_parser_t *parser, lxb_css_memory_t *memory) +{ + lxb_css_parser_memory_set(parser, memory); +} + +lxb_css_selectors_t * +lxb_css_parser_selectors_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_selectors(parser); +} + +void +lxb_css_parser_selectors_set_noi(lxb_css_parser_t *parser, + lxb_css_selectors_t *selectors) +{ + lxb_css_parser_selectors_set(parser, selectors); +} + +bool +lxb_css_parser_is_running_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_is_running(parser); +} + +bool +lxb_css_parser_status_is_unexpected_data_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_status_is_unexpected_data(parser); +} + +void +lxb_css_parser_failed_set_noi(lxb_css_parser_t *parser, bool is) +{ + lxb_css_parser_failed_set(parser, is); +} + +void +lxb_css_parser_failed_set_by_id_noi(lxb_css_parser_t *parser, int idx, bool is) +{ + lxb_css_parser_failed_set_by_id(parser, idx, is); +} + +bool +lxb_css_parser_is_failed_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_is_failed(parser); +} + +void +lxb_css_parser_set_ok_noi(lxb_css_parser_t *parser) +{ + lxb_css_parser_set_ok(parser); +} + +const lxb_char_t * +lxb_css_parser_buffer_noi(lxb_css_parser_t *parser, size_t *length) +{ + return lxb_css_parser_buffer(parser, length); +} + +void +lxb_css_parser_buffer_set_noi(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + lxb_css_parser_buffer_set(parser, data, length); +} + +lxb_css_parser_state_f +lxb_css_parser_state_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_state(parser); +} + +void +lxb_css_parser_state_set_noi(lxb_css_parser_t *parser, lxb_css_parser_state_f state) +{ + lxb_css_parser_state_set(parser, state); +} + +lxb_css_syntax_rule_t * +lxb_css_parser_current_rule_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_current_rule(parser); +} + +size_t +lxb_css_parser_rule_deep_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_rule_deep(parser); +} + +lxb_css_parser_state_t * +lxb_css_parser_states_pop_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_states_pop(parser); +} + +lxb_css_parser_state_t * +lxb_css_parser_states_to_root_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_states_to_root(parser); +} + +bool +lxb_css_parser_states_set_back_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_states_set_back(parser); +} + +void +lxb_css_parser_states_clean_noi(lxb_css_parser_t *parser) +{ + lxb_css_parser_states_clean(parser); +} + +lxb_css_parser_state_t * +lxb_css_parser_states_current_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_states_current(parser); +} + +void +lxb_css_parser_states_set_noi(lxb_css_parser_state_t *states, + lxb_css_parser_state_f state, void *context) +{ + lxb_css_parser_states_set(states, state, context); +} + +void +lxb_css_parser_states_up_noi(lxb_css_parser_t *parser) +{ + lxb_css_parser_states_up(parser); +} + +void +lxb_css_parser_states_down_noi(lxb_css_parser_t *parser) +{ + lxb_css_parser_states_down(parser); +} + +lxb_css_log_t * +lxb_css_parser_log_noi(lxb_css_parser_t *parser) +{ + return lxb_css_parser_log(parser); +} + +const lxb_css_syntax_token_t * +lxb_css_parser_token_end_noi(lxb_css_parser_t *parser, size_t offset) +{ + return lxb_css_parser_token_end(parser, offset); +} + +void +lxb_css_parser_set_context_noi(lxb_css_parser_t *parser, void *ctx) +{ + lxb_css_parser_set_context(parser, ctx); +} + +void +lxb_css_syntax_set_return_noi(lxb_css_parser_t *parser, void *value) +{ + lxb_css_syntax_set_return(parser, value); +} + +void * +lxb_css_syntax_returned_noi(lxb_css_parser_t *parser) +{ + return lxb_css_syntax_returned(parser); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.h new file mode 100644 index 0000000..c4e906a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/parser.h @@ -0,0 +1,756 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_PARSER_H +#define LEXBOR_CSS_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/log.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/selectors/selectors.h" + + +#define lxb_css_parser_token_m(parser, token) \ + do { \ + token = lxb_css_syntax_parser_token(parser); \ + if (token == NULL) { \ + return lxb_css_parser_fail((parser), (parser)->tkz->status); \ + } \ + } \ + while (false) + +#define lxb_css_parser_token_next_m(parser, token) \ + do { \ + token = lxb_css_syntax_token_next((parser)->tkz); \ + if (token == NULL) { \ + return lxb_css_parser_fail((parser), (parser)->tkz->status); \ + } \ + } \ + while (false) + +#define lxb_css_parser_token_wo_ws_m(parser, token) \ + do { \ + token = lxb_css_syntax_parser_token(parser); \ + if (token == NULL) { \ + return lxb_css_parser_fail((parser), (parser)->tkz->status); \ + } \ + \ + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { \ + lxb_css_syntax_parser_consume(parser); \ + token = lxb_css_syntax_parser_token(parser); \ + if (token == NULL) { \ + return lxb_css_parser_fail((parser), (parser)->tkz->status); \ + } \ + } \ + } \ + while (false) + +#define lxb_css_parser_token_status_m(parser, token) \ + do { \ + if ((token = lxb_css_syntax_parser_token(parser)) == NULL) { \ + return parser->tkz->status; \ + } \ + } \ + while (false) + +#define lxb_css_parser_token_status_next_m(parser, token) \ + do { \ + token = lxb_css_syntax_token_next((parser)->tkz); \ + if (token == NULL) { \ + return parser->tkz->status; \ + } \ + } \ + while (false) + +#define lxb_css_parser_token_status_wo_ws_m(parser, token) \ + do { \ + if ((token = lxb_css_syntax_parser_token(parser)) == NULL) { \ + return parser->tkz->status; \ + } \ + \ + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { \ + lxb_css_syntax_parser_consume(parser); \ + if ((token = lxb_css_syntax_parser_token(parser)) == NULL) { \ + return parser->tkz->status; \ + } \ + } \ + } \ + while (false) + +#define lxb_css_parser_string_dup_m(parser, token, _str, mraw) \ + do { \ + if (lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token),\ + (_str), (mraw)) != LXB_STATUS_OK) \ + { \ + return lxb_css_parser_fail((parser), \ + LXB_STATUS_ERROR_MEMORY_ALLOCATION); \ + } \ + } \ + while (false) + + +enum { + LXB_CSS_SYNTAX_PARSER_ERROR_UNDEF = 0x0000, + /* eof-in-at-rule */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOINATRU, + /* eof-in-qualified-rule */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOINQURU, + /* eof-in-simple-block */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOINSIBL, + /* eof-in-function */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOINFU, + /* eof-before-parse-rule */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOBEPARU, + /* unexpected-token-after-parse-rule */ + LXB_CSS_SYNTAX_PARSER_ERROR_UNTOAFPARU, + /* eof-before-parse-component-value */ + LXB_CSS_SYNTAX_PARSER_ERROR_EOBEPACOVA, + /* unexpected-token-after-parse-component-value */ + LXB_CSS_SYNTAX_PARSER_ERROR_UNTOAFPACOVA, + /* unexpected-token-in-declaration */ + LXB_CSS_SYNTAX_PARSER_ERROR_UNTOINDE, +}; + +typedef enum { + LXB_CSS_PARSER_CLEAN = 0, + LXB_CSS_PARSER_RUN, + LXB_CSS_PARSER_STOP, + LXB_CSS_PARSER_END +} +lxb_css_parser_stage_t; + +struct lxb_css_parser { + /* Modules */ + lxb_css_syntax_tokenizer_t *tkz; + lxb_css_selectors_t *selectors; + + /* Memory for all structures. */ + lxb_css_memory_t *memory; + + /* Syntax parse rules. */ + lxb_css_syntax_rule_t *rules_begin; + lxb_css_syntax_rule_t *rules_end; + lxb_css_syntax_rule_t *rules; + + /* States */ + lxb_css_parser_state_t *states_begin; + lxb_css_parser_state_t *states_end; + lxb_css_parser_state_t *states; + + /* Types */ + lxb_css_syntax_token_type_t *types_begin; + lxb_css_syntax_token_type_t *types_end; + lxb_css_syntax_token_type_t *types_pos; + + lxb_css_syntax_token_t token_end; + + lexbor_str_t str; + size_t str_size; + + lxb_css_log_t *log; + + lxb_css_parser_stage_t stage; + + lxb_css_syntax_declaration_offset_t offset; + + bool loop; + bool fake_null; + bool my_tkz; + + lxb_status_t status; +}; + +struct lxb_css_parser_state { + lxb_css_parser_state_f state; + void *context; + bool root; +}; + +struct lxb_css_parser_error { + lexbor_str_t message; +}; + + +/* + * Create a new CSS parser object. + * + * @return lxb_css_parser_t* if successful, otherwise NULL. + */ +LXB_API lxb_css_parser_t * +lxb_css_parser_create(void); + +/* + * Initialize the CSS parser with a tokenizer. + * + * @param[in] parser Required. The CSS parser object. + * If NULL, the function returns + * LXB_STATUS_ERROR_OBJECT_IS_NULL. + * @param[in] tkz Optional. The tokenizer object. + * If NULL, a new tokenizer will be created and used. + * (In that case the parser manages the tokenizer lifetime.) + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_css_parser_init(lxb_css_parser_t *parser, lxb_css_syntax_tokenizer_t *tkz); + +/* + * Initialize the Selectors module in the parser. + * + * Note: + * Be sure to destroy the Selectors object you created yourself using + * the lxb_css_parser_selectors_destroy() function. + * + * @param[in] parser Required. The CSS parser object. + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_css_parser_selectors_init(lxb_css_parser_t *parser); + +/* + * Destroy the selectors module in the parser. + * + * @param[in] parser Required. The CSS parser object. + */ +LXB_API void +lxb_css_parser_selectors_destroy(lxb_css_parser_t *parser); + +/* + * Clean the parser object. + * + * This resets internal parser state, but keeps allocated memory so the parser + * can be reused. + * + * @param[in] parser Required. The CSS parser object. + */ +LXB_API void +lxb_css_parser_clean(lxb_css_parser_t *parser); + +/* + * Erase the parser object. + * + * This resets internal parser state and releases internal allocations owned by + * the parser so it can be re-initialized. + * + * @param[in] parser Required. The CSS parser object. + */ +LXB_API void +lxb_css_parser_erase(lxb_css_parser_t *parser); + +/* + * Destroy the parser object. + * + * @param[in] parser Optional. The CSS parser object. + * If NULL, the function returns NULL. + * @param[in] self_destroy Optional flag. + * If true, also frees the parser object itself. + * If false, only inner resources are freed and the + * parser object remains valid. + * + * @return parser if self_destroy is false, otherwise NULL. + */ +LXB_API lxb_css_parser_t * +lxb_css_parser_destroy(lxb_css_parser_t *parser, bool self_destroy); + +/* + * Stop the parser main loop. + * + * This is a helper for state callbacks that want to immediately stop parsing + * without marking the current rule as failed. + * + * @param[in] parser Required. The CSS parser object. + * + * @return true (convenience return value, useful for state callbacks). + */ +LXB_API bool +lxb_css_parser_stop(lxb_css_parser_t *parser); + +/* + * Fail the parser with a status and stop the main loop. + * + * This is used for hard errors (OOM, overflow, etc.). It sets parser->status + * and stops the parser loop. + * + * @param[in] parser Required. The CSS parser object. + * @param[in] status Required. Status code describing the failure. + * + * @return true (convenience return value, useful for state callbacks). + */ +LXB_API bool +lxb_css_parser_fail(lxb_css_parser_t *parser, lxb_status_t status); + +/* + * Mark the current token as unexpected. + * + * This is a specialized helper for the common "unexpected data" parse error. + * It sets parser->status to LXB_STATUS_ERROR_UNEXPECTED_DATA and marks the + * current rule as failed. + * + * @param[in] parser Required. The CSS parser object. + * + * @return true (convenience return value, useful for state callbacks). + */ +LXB_API bool +lxb_css_parser_unexpected(lxb_css_parser_t *parser); + +/* + * Mark parsing of the current grammar/rule as successful. + * + * Why this function exists: + * - Most callbacks in the CSS parser work as a state machine: each callback + * receives the current token and returns bool to decide what to do next. + * - "Success" is not just a bool. The parser must switch to a special + * lxb_css_state_success state to properly handle trailing whitespace and + * the end-of-input token. + * - If you don't consume all tokens up to LXB_CSS_SYNTAX_TOKEN__END in the + * current rule and call this function, the parser will switch to "failed" + * mode. This is the same as calling the lxb_css_parser_failed() function. + * The exception is the LXB_CSS_SYNTAX_TOKEN_WHITESPACE token. If you are + * sure that only this token remains, the parser will simply exclude it. + * + * What it does: + * - Sets the current rule state to lxb_css_state_success. + * - Does not stop the parser loop by itself. + * + * Typical usage: + * - In css/property/state.c, after you have parsed and consumed the last token + * of a property value, you do: + * return lxb_css_parser_success(parser); + * - That transfers control to lxb_css_state_success, which consumes any + * whitespace and completes on LXB_CSS_SYNTAX_TOKEN__END. + * + * @param[in] parser Required. The CSS parser object. + * + * @return true (convenience return value, useful for state callbacks). + */ +LXB_API bool +lxb_css_parser_success(lxb_css_parser_t *parser); + +/* + * Mark parsing of the current grammar/rule as failed. + * + * Why this function exists: + * - In the state machine, failure is also not "just return false". The parser + * needs to switch to a rule-specific recovery state so it can safely skip + * input until it is allowed to stop (usually until end-of-input or a known + * synchronisation point). + * + * What it does: + * - Sets the current rule state to the rule-specific "failed" handler + * (rule->cbx.cb->failed). + * - Sets rule->failed = true. + * - Does not directly change parser->status. Use lxb_css_parser_fail() or + * lxb_css_parser_unexpected() when you need a concrete status code. + * + * Typical usage: + * - In css/property/state.c, if a token doesn't match the expected grammar, + * you do: + * return lxb_css_parser_failed(parser); + * - That switches the parser into the failed handler. For properties this lets + * the parser consume/skip tokens until it reaches LXB_CSS_SYNTAX_TOKEN__END. + * (See lxb_css_state_failed(): it consumes tokens until end, then calls + * lxb_css_parser_success() to finish.) + * + * Important! + * The user sets the callback to "failed" and must decide what to do in this + * situation. If the user does not consume all tokens before + * LXB_CSS_SYNTAX_TOKEN__END, there will be an infinite loop. + * + * @param[in] parser Required. The CSS parser object. + * + * @return true (convenience return value, useful for state callbacks). + */ + +LXB_API bool +lxb_css_parser_failed(lxb_css_parser_t *parser); + +LXB_API lxb_status_t +lxb_css_parser_unexpected_status(lxb_css_parser_t *parser); + +LXB_API bool +lxb_css_parser_unexpected_data(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +LXB_API lxb_status_t +lxb_css_parser_unexpected_data_status(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +LXB_API void * +lxb_css_parser_memory_fail_null(lxb_css_parser_t *parser); + +LXB_API bool +lxb_css_parser_memory_fail(lxb_css_parser_t *parser); + +LXB_API lxb_status_t +lxb_css_parser_memory_fail_status(lxb_css_parser_t *parser); + +/* + * Push a new state to the parser. + * + * @param[in] parser Required. The CSS parser object. + * @param[in] state Required. State callback to execute. + * @param[in] context Optional user pointer associated with this state. + * Stored as-is; the parser doesn't own it. + * @param[in] stop If true, the pushed state is marked as a "stop point". + * This is used by the parser to stop/return control when + * this state is reached. + * + * @return lxb_css_parser_state_t* if successful, otherwise NULL. + */ +LXB_API lxb_css_parser_state_t * +lxb_css_parser_states_push(lxb_css_parser_t *parser, + lxb_css_parser_state_f state, + void *context, bool stop); + +/* + * Go to the next state in the parser. + * + * @param[in] parser Required. The CSS parser object. + * @param[in] next Required. Next state callback. + * @param[in] back Required. State callback to return to. + * @param[in] ctx Optional user pointer for the next state. + * Stored as-is; the parser doesn't own it. + * @param[in] root If true, marks this state as a "root" state. Root states + * are used by helper functions (e.g. to rewind to root). + * + * @return lxb_css_parser_state_t* if successful, otherwise NULL. + */ +LXB_API lxb_css_parser_state_t * +lxb_css_parser_states_next(lxb_css_parser_t *parser, + lxb_css_parser_state_f next, + lxb_css_parser_state_f back, void *ctx, bool root); + +/* + * Push a token type to the parser. + * + * @param[in] parser Required. The CSS parser object. + * @param[in] type Required. Token type to push. + * + * @return LXB_STATUS_OK if successful, otherwise an error status value. + */ +LXB_API lxb_status_t +lxb_css_parser_types_push(lxb_css_parser_t *parser, + lxb_css_syntax_token_type_t type); + + +/* + * Inline functions + */ +lxb_inline lxb_status_t +lxb_css_parser_status(lxb_css_parser_t *parser) +{ + return parser->status; +} + +lxb_inline lxb_css_memory_t * +lxb_css_parser_memory(lxb_css_parser_t *parser) +{ + return parser->memory; +} + +lxb_inline void +lxb_css_parser_memory_set(lxb_css_parser_t *parser, lxb_css_memory_t *memory) +{ + parser->memory = memory; +} + +lxb_inline lxb_css_selectors_t * +lxb_css_parser_selectors(lxb_css_parser_t *parser) +{ + return parser->selectors; +} + +lxb_inline void +lxb_css_parser_selectors_set(lxb_css_parser_t *parser, + lxb_css_selectors_t *selectors) +{ + parser->selectors = selectors; +} + +lxb_inline bool +lxb_css_parser_is_running(lxb_css_parser_t *parser) +{ + return parser->stage == LXB_CSS_PARSER_RUN; +} + +lxb_inline bool +lxb_css_parser_status_is_unexpected_data(lxb_css_parser_t *parser) +{ + return parser->status == LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +lxb_inline void +lxb_css_parser_failed_set(lxb_css_parser_t *parser, bool is) +{ + parser->rules->failed = is; +} + +lxb_inline void +lxb_css_parser_failed_set_by_id(lxb_css_parser_t *parser, int idx, bool is) +{ + lxb_css_syntax_rule_t *rules = parser->rules + idx; + + if (rules > parser->rules_begin && rules < parser->rules_end) { + rules->failed = is; + } +} + +lxb_inline bool +lxb_css_parser_is_failed(lxb_css_parser_t *parser) +{ + return parser->rules->failed; +} + +lxb_inline void +lxb_css_parser_set_ok(lxb_css_parser_t *parser) +{ + parser->rules->failed = false; + parser->status = LXB_STATUS_OK; +} + +lxb_inline const lxb_char_t * +lxb_css_parser_buffer(lxb_css_parser_t *parser, size_t *length) +{ + if (length != NULL) { + *length = parser->tkz->in_end - parser->tkz->in_begin; + } + + return parser->tkz->in_begin; +} + +lxb_inline void +lxb_css_parser_buffer_set(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + lxb_css_syntax_tokenizer_buffer_set(parser->tkz, data, length); +} + +lxb_inline lxb_css_parser_state_f +lxb_css_parser_state(lxb_css_parser_t *parser) +{ + return parser->rules->state; +} + +lxb_inline void +lxb_css_parser_state_set(lxb_css_parser_t *parser, lxb_css_parser_state_f state) +{ + parser->rules->state = state; +} + +lxb_inline lxb_css_syntax_rule_t * +lxb_css_parser_current_rule(lxb_css_parser_t *parser) +{ + return parser->rules; +} + +lxb_inline size_t +lxb_css_parser_rule_deep(lxb_css_parser_t *parser) +{ + return parser->rules->deep; +} + +lxb_inline lxb_css_parser_state_t * +lxb_css_parser_states_pop(lxb_css_parser_t *parser) +{ + return parser->states--; +} + +lxb_inline lxb_css_parser_state_t * +lxb_css_parser_states_to_root(lxb_css_parser_t *parser) +{ + lxb_css_parser_state_t *entry = parser->states; + + while (!entry->root) { + entry--; + } + + parser->states = entry; + + return entry; +} + +lxb_inline bool +lxb_css_parser_states_set_back(lxb_css_parser_t *parser) +{ + const lxb_css_parser_state_t *entry = parser->states; + lxb_css_syntax_rule_t *rules = parser->rules; + + rules->state = entry->state; + rules->context = entry->context; + + return true; +} + +lxb_inline void +lxb_css_parser_states_clean(lxb_css_parser_t *parser) +{ + parser->states = parser->states_begin; +} + +lxb_inline lxb_css_parser_state_t * +lxb_css_parser_states_current(lxb_css_parser_t *parser) +{ + return parser->states; +} + +lxb_inline void +lxb_css_parser_states_set(lxb_css_parser_state_t *states, + lxb_css_parser_state_f state, void *context) +{ + states->state = state; + states->context = context; +} + +lxb_inline void +lxb_css_parser_states_up(lxb_css_parser_t *parser) +{ + parser->states++; +} + +lxb_inline void +lxb_css_parser_states_down(lxb_css_parser_t *parser) +{ + parser->states--; +} + +lxb_inline lxb_css_log_t * +lxb_css_parser_log(lxb_css_parser_t *parser) +{ + return parser->log; +} + +lxb_inline const lxb_css_syntax_token_t * +lxb_css_parser_token_end(lxb_css_parser_t *parser, size_t offset) +{ + parser->token_end.offset = offset; + return &parser->token_end; +} + +lxb_inline void +lxb_css_parser_set_context(lxb_css_parser_t *parser, void *ctx) +{ + parser->rules->context = ctx; +} + +lxb_inline void +lxb_css_syntax_set_return(lxb_css_parser_t *parser, void *value) +{ + parser->rules[-1].returned = value; +} + +lxb_inline void * +lxb_css_syntax_returned(lxb_css_parser_t *parser) +{ + return parser->rules->returned; +} + +/* + * No-inline functions for ABI. + */ +LXB_API lxb_status_t +lxb_css_parser_status_noi(lxb_css_parser_t *parser); + +LXB_API lxb_css_memory_t * +lxb_css_parser_memory_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_memory_set_noi(lxb_css_parser_t *parser, lxb_css_memory_t *memory); + +LXB_API lxb_css_selectors_t * +lxb_css_parser_selectors_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_selectors_set_noi(lxb_css_parser_t *parser, + lxb_css_selectors_t *selectors); + +LXB_API bool +lxb_css_parser_is_running_noi(lxb_css_parser_t *parser); + +LXB_API bool +lxb_css_parser_status_is_unexpected_data_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_failed_set_noi(lxb_css_parser_t *parser, bool is); + +LXB_API void +lxb_css_parser_failed_set_by_id_noi(lxb_css_parser_t *parser, int idx, bool is); + +LXB_API bool +lxb_css_parser_is_failed_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_set_ok_noi(lxb_css_parser_t *parser); + +LXB_API const lxb_char_t * +lxb_css_parser_buffer_noi(lxb_css_parser_t *parser, size_t *length); + +LXB_API void +lxb_css_parser_buffer_set_noi(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_parser_state_f +lxb_css_parser_state_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_state_set_noi(lxb_css_parser_t *parser, + lxb_css_parser_state_f state); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_parser_current_rule_noi(lxb_css_parser_t *parser); + +LXB_API size_t +lxb_css_parser_rule_deep_noi(lxb_css_parser_t *parser); + +LXB_API lxb_css_parser_state_t * +lxb_css_parser_states_pop_noi(lxb_css_parser_t *parser); + +LXB_API lxb_css_parser_state_t * +lxb_css_parser_states_to_root_noi(lxb_css_parser_t *parser); + +LXB_API bool +lxb_css_parser_states_set_back_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_states_clean_noi(lxb_css_parser_t *parser); + +LXB_API lxb_css_parser_state_t * +lxb_css_parser_states_current_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_states_set_noi(lxb_css_parser_state_t *states, + lxb_css_parser_state_f state, void *context); + +LXB_API void +lxb_css_parser_states_up_noi(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_parser_states_down_noi(lxb_css_parser_t *parser); + +LXB_API lxb_css_log_t * +lxb_css_parser_log_noi(lxb_css_parser_t *parser); + +LXB_API const lxb_css_syntax_token_t * +lxb_css_parser_token_end_noi(lxb_css_parser_t *parser, size_t offset); + +LXB_API void +lxb_css_parser_set_context_noi(lxb_css_parser_t *parser, void *ctx); + +LXB_API void +lxb_css_syntax_set_return_noi(lxb_css_parser_t *parser, void *value); + +LXB_API void * +lxb_css_syntax_returned_noi(lxb_css_parser_t *parser); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_PARSER_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.c new file mode 100644 index 0000000..6e460c3 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.c @@ -0,0 +1,3171 @@ +/* + * Copyright (C) 2021-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/property.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/stylesheet.h" +#include "lexbor/css/property/state.h" +#include "lexbor/css/property/res.h" +#include "lexbor/core/serialize.h" +#include "lexbor/core/conv.h" + + +const lxb_css_entry_data_t * +lxb_css_property_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_property_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_entry_data_t * +lxb_css_property_by_id(uintptr_t id) +{ + return &lxb_css_property_data[id]; +} + +const void * +lxb_css_property_initial_by_id(uintptr_t id) +{ + if (id >= LXB_CSS_PROPERTY__LAST_ENTRY) { + return NULL; + } + + return lxb_css_property_data[id].initial; +} + +void * +lxb_css_property_destroy(lxb_css_memory_t *memory, void *style, + lxb_css_property_type_t type, bool self_destroy) +{ + const lxb_css_entry_data_t *data; + + data = lxb_css_property_by_id(type); + if (data == NULL) { + return style; + } + + return data->destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_serialize(const void *style, lxb_css_property_type_t type, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_entry_data_t *data; + + data = lxb_css_property_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + return data->serialize(style, cb, ctx); +} + +lxb_status_t +lxb_css_property_serialize_str(const void *style, lxb_css_property_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str) +{ + const lxb_css_entry_data_t *data; + + data = lxb_css_property_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + return lxb_css_serialize_str_handler(style, str, mraw, data->serialize); +} + +lxb_status_t +lxb_css_property_serialize_name(const void *style, lxb_css_property_type_t type, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_entry_data_t *data; + + switch (type) { + case LXB_CSS_PROPERTY__UNDEF: + return lxb_css_property__undef_serialize_name(style, cb, ctx); + + case LXB_CSS_PROPERTY__CUSTOM: + return lxb_css_property__custom_serialize_name(style, cb, ctx); + + default: + break; + } + + data = lxb_css_property_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + return cb(data->name, data->length, ctx); +} + +lxb_status_t +lxb_css_property_serialize_name_str(const void *style, lxb_css_property_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str) +{ + const lxb_css_entry_data_t *data; + + switch (type) { + case LXB_CSS_PROPERTY__UNDEF: + return lxb_css_serialize_str_handler(style, str, mraw, + lxb_css_property__undef_serialize_name); + + case LXB_CSS_PROPERTY__CUSTOM: + return lxb_css_serialize_str_handler(style, str, mraw, + lxb_css_property__custom_serialize_name); + + default: + break; + } + + data = lxb_css_property_by_id(type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + if (str->data == NULL) { + lexbor_str_init(str, mraw, data->length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + (void) lexbor_str_append(str, mraw, data->name, data->length); + + return LXB_STATUS_OK; +} + +/* _undef. */ + +void * +lxb_css_property__undef_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property__undef_t)); +} + +void * +lxb_css_property__undef_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + if (style == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_mraw_free(memory->mraw, style); + } + + return style; +} + +lxb_status_t +lxb_css_property__undef_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__undef_t *undef = style; + + return cb(undef->value.data, undef->value.length, ctx); +} + +lxb_status_t +lxb_css_property__undef_serialize_name(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__undef_t *undef = style; + const lxb_css_entry_data_t *data; + + if (undef->type == LXB_CSS_PROPERTY__UNDEF) { + return LXB_STATUS_OK; + } + + data = lxb_css_property_by_id(undef->type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + return cb(data->name, data->length, ctx); +} + +lxb_status_t +lxb_css_property__undef_serialize_value(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__undef_t *undef = style; + + if (undef->type == LXB_CSS_PROPERTY__UNDEF) { + return cb(undef->value.data, undef->value.length, ctx); + } + + return LXB_STATUS_OK; +} + +/* _custom. */ + +void * +lxb_css_property__custom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property__custom_t)); +} + +void * +lxb_css_property__custom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + if (style == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_mraw_free(memory->mraw, style); + } + + return style; +} + +lxb_status_t +lxb_css_property__custom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__custom_t *custom = style; + + if (custom->value.data == NULL) { + return LXB_STATUS_OK; + } + + return cb(custom->value.data, custom->value.length, ctx); +} + +lxb_status_t +lxb_css_property__custom_serialize_name(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__custom_t *custom = style; + + return cb(custom->name.data, custom->name.length, ctx); +} + +lxb_status_t +lxb_css_property__custom_serialize_value(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property__custom_t *custom = style; + + if (custom->value.data == NULL) { + return LXB_STATUS_OK; + } + + return cb(custom->value.data, custom->value.length, ctx); +} + +/* Display. */ + +void * +lxb_css_property_display_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_display_t)); +} + +void * +lxb_css_property_display_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_display_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_data_t *data; + const lxb_css_property_display_t *display = property; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + data = lxb_css_value_by_id(display->a); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, data->name, data->length, ctx, status); + + if (display->b == LXB_CSS_PROPERTY__UNDEF) { + return LXB_STATUS_OK; + } + + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + data = lxb_css_value_by_id(display->b); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, data->name, data->length, ctx, status); + + if (display->c == LXB_CSS_PROPERTY__UNDEF) { + return LXB_STATUS_OK; + } + + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + data = lxb_css_value_by_id(display->c); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, data->name, data->length, ctx, status); + + return LXB_STATUS_OK; +} + +/* Order. */ + +void * +lxb_css_property_order_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_order_t)); +} + +void * +lxb_css_property_order_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_order_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_integer_type_sr(style, cb, ctx); +} + +/* Visibility. */ + +void * +lxb_css_property_visibility_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_visibility_t)); +} + +void * +lxb_css_property_visibility_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_visibility_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_visibility_t *vb = style; + + return lxb_css_value_serialize(vb->type, cb, ctx); +} + +/* Width. */ + +void * +lxb_css_property_width_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_width_t)); +} + +void * +lxb_css_property_width_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_width_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_width_t *width = property; + + switch (width->type) { + case LXB_CSS_VALUE__LENGTH: + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_length_sr(&width->u.length, cb, ctx); + + case LXB_CSS_VALUE__PERCENTAGE: + return lxb_css_value_percentage_sr(&width->u.percentage, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(width->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* Height. */ + +void * +lxb_css_property_height_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_height_t)); +} + +void * +lxb_css_property_height_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_height_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(property, cb, ctx); +} + +/* Box-sizing. */ + +void * +lxb_css_property_box_sizing_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_box_sizing_t)); +} + +void * +lxb_css_property_box_sizing_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_box_sizing_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_box_sizing_t *bsize = property; + + return lxb_css_value_serialize(bsize->type, cb, ctx); +} + +/* Min-width. */ + +void * +lxb_css_property_min_width_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_min_width_t)); +} + +void * +lxb_css_property_min_width_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_min_width_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(property, cb, ctx); +} + +/* Min-height. */ + +void * +lxb_css_property_min_height_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_min_height_t)); +} + +void * +lxb_css_property_min_height_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_min_height_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(property, cb, ctx); +} + +/* Max-width. */ + +void * +lxb_css_property_max_width_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_max_width_t)); +} + +void * +lxb_css_property_max_width_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_max_width_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(property, cb, ctx); +} + +/* Max-height. */ + +void * +lxb_css_property_max_height_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_max_height_t)); +} + +void * +lxb_css_property_max_height_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_max_height_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(property, cb, ctx); +} + +/* Margin. */ + +void * +lxb_css_property_margin_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_margin_t)); +} + +void * +lxb_css_property_margin_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_margin_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_margin_t *margin = property; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + /* Top. */ + + status = lxb_css_value_length_percentage_sr(&margin->top, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (margin->right.type == LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_OK; + } + + /* Right. */ + + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + status = lxb_css_value_length_percentage_sr(&margin->right, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (margin->bottom.type == LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_OK; + } + + /* Bottom. */ + + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + status = lxb_css_value_length_percentage_sr(&margin->bottom, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (margin->left.type == LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_OK; + } + + /* Left. */ + + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + return lxb_css_value_length_percentage_sr(&margin->left, cb, ctx); +} + +/* Margin-top. */ + +void * +lxb_css_property_margin_top_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_margin_top_t)); +} + +void * +lxb_css_property_margin_top_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_margin_top_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Margin-right. */ + +void * +lxb_css_property_margin_right_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_margin_right_t)); +} + +void * +lxb_css_property_margin_right_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_margin_right_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Margin-bottom. */ + +void * +lxb_css_property_margin_bottom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_margin_bottom_t)); +} + +void * +lxb_css_property_margin_bottom_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_margin_bottom_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Margin-left. */ + +void * +lxb_css_property_margin_left_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_margin_left_t)); +} + +void * +lxb_css_property_margin_left_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_margin_left_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Padding. */ + +void * +lxb_css_property_padding_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_padding_t)); +} + +void * +lxb_css_property_padding_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_padding_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_margin_serialize(property, cb, ctx); +} + +/* Padding-top. */ + +void * +lxb_css_property_padding_top_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_padding_top_t)); +} + +void * +lxb_css_property_padding_top_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_padding_top_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Padding-right. */ + +void * +lxb_css_property_padding_right_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_padding_right_t)); +} + +void * +lxb_css_property_padding_right_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_padding_right_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Padding-bottom. */ + +void * +lxb_css_property_padding_bottom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_padding_bottom_t)); +} + +void * +lxb_css_property_padding_bottom_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_padding_bottom_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Padding-left. */ + +void * +lxb_css_property_padding_left_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_padding_left_t)); +} + +void * +lxb_css_property_padding_left_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_padding_left_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(property, cb, ctx); +} + +/* Border. */ + +void * +lxb_css_property_border_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_border_t)); +} + +void * +lxb_css_property_border_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_border_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool ws_print; + lxb_status_t status; + const lxb_css_property_border_t *border = property; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + ws_print = false; + + if (border->width.type != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_length_type_sr(&border->width, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (border->style != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(border->style, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (border->color.type != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_value_color_serialize(&border->color, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* Border-top. */ + +void * +lxb_css_property_border_top_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_border_top_t)); +} + +void * +lxb_css_property_border_top_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_border_top_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_border_serialize(property, cb, ctx); +} + +/* Border-right. */ + +void * +lxb_css_property_border_right_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_border_right_t)); +} + +void * +lxb_css_property_border_right_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_border_right_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_border_serialize(property, cb, ctx); +} + +/* Border-bottom. */ + +void * +lxb_css_property_border_bottom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_border_bottom_t)); +} + +void * +lxb_css_property_border_bottom_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_border_bottom_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_border_serialize(property, cb, ctx); +} + +/* Border-left. */ + +void * +lxb_css_property_border_left_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_border_left_t)); +} + +void * +lxb_css_property_border_left_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_border_left_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_border_serialize(property, cb, ctx); +} + +void * +lxb_css_property_border_top_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_border_top_color_t)); +} + +void * +lxb_css_property_border_top_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_border_top_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(style, cb, ctx); +} + +void * +lxb_css_property_border_right_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_border_right_color_t)); +} + +void * +lxb_css_property_border_right_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_border_right_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(style, cb, ctx); +} + +void * +lxb_css_property_border_bottom_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_border_bottom_color_t)); +} + +void * +lxb_css_property_border_bottom_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_border_bottom_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(style, cb, ctx); +} + +void * +lxb_css_property_border_left_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_border_left_color_t)); +} + +void * +lxb_css_property_border_left_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_border_left_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(style, cb, ctx); +} + +void * +lxb_css_property_background_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_background_color_t)); +} + +void * +lxb_css_property_background_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_background_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(style, cb, ctx); +} + +/* Color. */ + +void * +lxb_css_property_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_color_t)); +} + +void * +lxb_css_property_color_destroy(lxb_css_memory_t *memory, + void *property, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, property, self_destroy); +} + +lxb_status_t +lxb_css_property_color_serialize(const void *property, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_color_serialize(property, cb, ctx); +} + +void * +lxb_css_property_opacity_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_opacity_t)); +} + +void * +lxb_css_property_opacity_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_opacity_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_percentage_sr(style, cb, ctx); +} + +void * +lxb_css_property_position_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_position_t)); +} + +void * +lxb_css_property_position_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_position_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_position_t *position = style; + + return lxb_css_value_serialize(position->type, cb, ctx); +} + +void * +lxb_css_property_top_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_top_t)); +} + +void * +lxb_css_property_top_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_top_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(style, cb, ctx); +} + +void * +lxb_css_property_right_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_right_t)); +} + +void * +lxb_css_property_right_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_right_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_bottom_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_bottom_t)); +} + +void * +lxb_css_property_bottom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_bottom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_left_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_left_t)); +} + +void * +lxb_css_property_left_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_left_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_inset_block_start_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_inset_block_start_t)); +} + +void * +lxb_css_property_inset_block_start_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_inset_block_start_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_inset_inline_start_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_inset_inline_start_t)); +} + +void * +lxb_css_property_inset_inline_start_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_inset_inline_start_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_inset_block_end_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_inset_block_end_t)); +} + +void * +lxb_css_property_inset_block_end_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_inset_block_end_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +void * +lxb_css_property_inset_inline_end_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_inset_inline_end_t)); +} + +void * +lxb_css_property_inset_inline_end_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_inset_inline_end_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_top_serialize(style, cb, ctx); +} + +/* Text-transform. */ + +LXB_API void * +lxb_css_property_text_transform_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_transform_t)); +} + +LXB_API void * +lxb_css_property_text_transform_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_transform_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool ws_print; + lxb_status_t status; + const lxb_css_property_text_transform_t *tt = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + ws_print = false; + + if (tt->type_case != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(tt->type_case, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (tt->full_width != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(tt->full_width, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (tt->full_size_kana != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_value_serialize(tt->full_size_kana, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* Text-align. */ + +LXB_API void * +lxb_css_property_text_align_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_align_t)); +} + +LXB_API void * +lxb_css_property_text_align_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_align_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_align_t *ta = style; + + return lxb_css_value_serialize(ta->type, cb, ctx); +} + +/* Text-align-all. */ + +LXB_API void * +lxb_css_property_text_align_all_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_align_all_t)); +} + +LXB_API void * +lxb_css_property_text_align_all_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_align_all_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_align_all_t *taa = style; + + return lxb_css_value_serialize(taa->type, cb, ctx); +} + +/* Text-align-last. */ + +LXB_API void * +lxb_css_property_text_align_last_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_align_last_t)); +} + +LXB_API void * +lxb_css_property_text_align_last_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_align_last_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_align_last_t *tal = style; + + return lxb_css_value_serialize(tal->type, cb, ctx); +} + +/* Text-justify. */ + +LXB_API void * +lxb_css_property_text_justify_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_justify_t)); +} + +LXB_API void * +lxb_css_property_text_justify_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_justify_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_justify_t *tj = style; + + return lxb_css_value_serialize(tj->type, cb, ctx); +} + +/* Text-indent. */ + +LXB_API void * +lxb_css_property_text_indent_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_indent_t)); +} + +LXB_API void * +lxb_css_property_text_indent_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_indent_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_text_indent_t *ti = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + if (ti->type == LXB_CSS_VALUE__LENGTH) { + status = lxb_css_value_length_percentage_sr(&ti->length, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + else if (ti->type != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(ti->type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + if (ti->hanging != LXB_CSS_VALUE__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + status = lxb_css_value_serialize(ti->hanging, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + if (ti->each_line != LXB_CSS_VALUE__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + return lxb_css_value_serialize(ti->each_line, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* White-space. */ + +LXB_API void * +lxb_css_property_white_space_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_white_space_t)); +} + +LXB_API void * +lxb_css_property_white_space_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_white_space_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_white_space_t *ws = style; + + return lxb_css_value_serialize(ws->type, cb, ctx); +} + +/* Tab-size. */ + +LXB_API void * +lxb_css_property_tab_size_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_tab_size_t)); +} + +LXB_API void * +lxb_css_property_tab_size_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_tab_size_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_length_sr(style, cb, ctx); +} + +/* Word-break. */ + +LXB_API void * +lxb_css_property_word_break_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_word_break_t)); +} + +LXB_API void * +lxb_css_property_word_break_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_word_break_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_word_break_t *wb = style; + + return lxb_css_value_serialize(wb->type, cb, ctx); +} + +/* Line-break. */ + +LXB_API void * +lxb_css_property_line_break_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_line_break_t)); +} + +LXB_API void * +lxb_css_property_line_break_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_line_break_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_line_break_t *lb = style; + + return lxb_css_value_serialize(lb->type, cb, ctx); +} + +/* Hyphens. */ + +LXB_API void * +lxb_css_property_hyphens_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_hyphens_t)); +} + +LXB_API void * +lxb_css_property_hyphens_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_hyphens_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_hyphens_t *hpns = style; + + return lxb_css_value_serialize(hpns->type, cb, ctx); +} + +/* Overflow-wrap. */ + +LXB_API void * +lxb_css_property_overflow_wrap_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_overflow_wrap_t)); +} + +LXB_API void * +lxb_css_property_overflow_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_overflow_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_overflow_wrap_t *ow = style; + + return lxb_css_value_serialize(ow->type, cb, ctx); +} + +/* Word-wrap. */ + +LXB_API void * +lxb_css_property_word_wrap_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_word_wrap_t)); +} + +LXB_API void * +lxb_css_property_word_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_word_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_word_wrap_t *ww = style; + + return lxb_css_value_serialize(ww->type, cb, ctx); +} + +/* Word-spacing. */ + +LXB_API void * +lxb_css_property_word_spacing_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_word_spacing_t)); +} + +LXB_API void * +lxb_css_property_word_spacing_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_word_spacing_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_type_sr(style, cb, ctx); +} + +/* Letter-spacing. */ + +LXB_API void * +lxb_css_property_letter_spacing_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_letter_spacing_t)); +} + +LXB_API void * +lxb_css_property_letter_spacing_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_letter_spacing_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_type_sr(style, cb, ctx); +} + +/* Hanging-punctuation. */ + +LXB_API void * +lxb_css_property_hanging_punctuation_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_hanging_punctuation_t)); +} + +LXB_API void * +lxb_css_property_hanging_punctuation_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_hanging_punctuation_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool ws_print; + lxb_status_t status; + const lxb_css_property_hanging_punctuation_t *hp = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + ws_print = false; + + if (hp->type_first != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(hp->type_first, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (hp->force_allow != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(hp->force_allow, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ws_print = true; + } + + if (hp->last != LXB_CSS_VALUE__UNDEF) { + if (ws_print) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_value_serialize(hp->last, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* Font-family. */ + +void * +lxb_css_property_font_family_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_font_family_t)); +} + +void * +lxb_css_property_font_family_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + lxb_css_property_font_family_t *ff = style; + lxb_css_property_family_name_t *name, *next; + + name = ff->first; + + while (name != NULL) { + next = name->next; + + if (!name->generic) { + (void) lexbor_str_destroy(&name->u.str, memory->mraw, false); + } + + lexbor_mraw_free(memory->mraw, name); + + name = next; + } + + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_font_family_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_font_family_t *ff = style; + const lxb_css_property_family_name_t *name; + + static const lexbor_str_t str_comma = lexbor_str(", "); + + name = ff->first; + + while (name != NULL) { + if (name->generic) { + status = lxb_css_value_serialize(name->u.type, cb, ctx); + } + else { + status = lxb_css_syntax_ident_or_string_serialize(name->u.str.data, + name->u.str.length, + cb, ctx); + } + + if (status != LXB_STATUS_OK) { + return status; + } + + name = name->next; + + if (name != NULL) { + lexbor_serialize_write(cb, str_comma.data, str_comma.length, + ctx, status); + } + } + + return LXB_STATUS_OK; +} + +/* Font-weight. */ + +void * +lxb_css_property_font_weight_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_font_weight_t)); +} + +void * +lxb_css_property_font_weight_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_font_weight_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_type_sr(style, cb, ctx); +} + +/* Font-stretch. */ + +void * +lxb_css_property_font_stretch_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_font_stretch_t)); +} + +void * +lxb_css_property_font_stretch_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_font_stretch_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_percentage_type_sr(style, cb, ctx); +} + +/* Font-style. */ + +void * +lxb_css_property_font_style_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_font_style_t)); +} + +void * +lxb_css_property_font_style_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +lxb_status_t +lxb_css_property_font_style_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_font_style_t *fs = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + status = lxb_css_value_serialize(fs->type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (fs->angle.unit != (lxb_css_unit_angle_t) LXB_CSS_UNIT__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + status = lxb_css_value_angle_sr(&fs->angle, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + return LXB_STATUS_OK; +} + +/* Font-size. */ + +LXB_API void * +lxb_css_property_font_size_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_font_size_t)); +} + +LXB_API void * +lxb_css_property_font_size_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_font_size_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_type_sr(style, cb, ctx); +} + +/* Float-reference. */ + +LXB_API void * +lxb_css_property_float_reference_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_float_reference_t)); +} + +LXB_API void * +lxb_css_property_float_reference_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_float_reference_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_float_reference_t *fr = style; + + return lxb_css_value_serialize(fr->type, cb, ctx); +} + +/* Float. */ + +LXB_API void * +lxb_css_property_float_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_float_t)); +} + +LXB_API void * +lxb_css_property_float_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_float_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_float_t *flt = style; + + static const lexbor_str_t str_o = lexbor_str("("); + static const lexbor_str_t str_cm = lexbor_str(", "); + static const lexbor_str_t str_c = lexbor_str(")"); + + status = lxb_css_value_serialize(flt->type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (flt->length.type != LXB_CSS_VALUE__LENGTH) { + return LXB_STATUS_OK; + } + + lexbor_serialize_write(cb, str_o.data, str_o.length, ctx, status); + + status = lxb_css_value_length_sr(&flt->length.length, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (flt->snap_type == LXB_CSS_VALUE__UNDEF) { + return cb(str_c.data, str_c.length, ctx); + } + + lexbor_serialize_write(cb, str_cm.data, str_cm.length, ctx, status); + + status = lxb_css_value_serialize(flt->snap_type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(str_c.data, str_c.length, ctx); +} + +/* Clear. */ + +LXB_API void * +lxb_css_property_clear_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_clear_t)); +} + +LXB_API void * +lxb_css_property_clear_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_clear_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_clear_t *cls = style; + + return lxb_css_value_serialize(cls->type, cb, ctx); +} + +/* Float-defer. */ + +LXB_API void * +lxb_css_property_float_defer_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_float_defer_t)); +} + +LXB_API void * +lxb_css_property_float_defer_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_float_defer_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_float_defer_t *def = style; + + if (def->type == LXB_CSS_FLOAT_DEFER__INTEGER) { + return lxb_css_value_integer_sr(&def->integer, cb, ctx); + } + + return lxb_css_value_serialize(def->type, cb, ctx); +} + +/* Float-offset. */ + +LXB_API void * +lxb_css_property_float_offset_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_float_offset_t)); +} + +LXB_API void * +lxb_css_property_float_offset_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_float_offset_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(style, cb, ctx); +} + +/* Wrap-flow. */ + +LXB_API void * +lxb_css_property_wrap_flow_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_wrap_flow_t)); +} + +LXB_API void * +lxb_css_property_wrap_flow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_wrap_flow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_wrap_flow_t *wf = style; + + return lxb_css_value_serialize(wf->type, cb, ctx); +} + +/* Wrap-through. */ + +LXB_API void * +lxb_css_property_wrap_through_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_wrap_through_t)); +} + +LXB_API void * +lxb_css_property_wrap_through_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_wrap_through_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_wrap_through_t *wt = style; + + return lxb_css_value_serialize(wt->type, cb, ctx); +} + +/* Flex-direction. */ + +LXB_API void * +lxb_css_property_flex_direction_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_direction_t)); +} + +LXB_API void * +lxb_css_property_flex_direction_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_direction_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_flex_direction_t *fd = style; + + return lxb_css_value_serialize(fd->type, cb, ctx); +} + +/* Flex-wrap. */ + +LXB_API void * +lxb_css_property_flex_wrap_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_wrap_t)); +} + +LXB_API void * +lxb_css_property_flex_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_flex_wrap_t *fw = style; + + return lxb_css_value_serialize(fw->type, cb, ctx); +} + +/* Flex-flow. */ + +LXB_API void * +lxb_css_property_flex_flow_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_flow_t)); +} + +LXB_API void * +lxb_css_property_flex_flow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_flow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_flex_flow_t *ff = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + if (ff->type_direction != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(ff->type_direction, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + if (ff->wrap != LXB_CSS_VALUE__UNDEF) { + if (ff->type_direction != LXB_CSS_VALUE__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_value_serialize(ff->wrap, cb, ctx); + } + + return LXB_STATUS_OK; +} + +/* Flex. */ + +LXB_API void * +lxb_css_property_flex_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_flex_t)); +} + +LXB_API void * +lxb_css_property_flex_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_flex_t *flex = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + if (flex->type != LXB_CSS_VALUE__UNDEF) { + return lxb_css_value_serialize(flex->type, cb, ctx); + } + + if (flex->grow.type != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_number_sr(&flex->grow.number, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (flex->shrink.type != LXB_CSS_VALUE__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + status = lxb_css_value_number_sr(&flex->shrink.number, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + } + + if (flex->basis.type == LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_OK; + } + + if (flex->grow.type != LXB_CSS_VALUE__UNDEF) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_property_flex_basis_serialize(&flex->basis, cb, ctx); +} + +/* Flex-grow. */ + +LXB_API void * +lxb_css_property_flex_grow_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_grow_t)); +} + +LXB_API void * +lxb_css_property_flex_grow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_grow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_type_sr(style, cb, ctx); +} + +/* Flex-shrink. */ + +LXB_API void * +lxb_css_property_flex_shrink_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_shrink_t)); +} + +LXB_API void * +lxb_css_property_flex_shrink_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_shrink_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_type_sr(style, cb, ctx); +} + +/* Flex-basis. */ + +LXB_API void * +lxb_css_property_flex_basis_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_flex_basis_t)); +} + +LXB_API void * +lxb_css_property_flex_basis_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_flex_basis_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_width_serialize(style, cb, ctx); +} + +/* Justify-content. */ + +LXB_API void * +lxb_css_property_justify_content_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_justify_content_t)); +} + +LXB_API void * +lxb_css_property_justify_content_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_justify_content_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_justify_content_t *jc = style; + + return lxb_css_value_serialize(jc->type, cb, ctx); +} + +/* Align-items. */ + +LXB_API void * +lxb_css_property_align_items_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_align_items_t)); +} + +LXB_API void * +lxb_css_property_align_items_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_align_items_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_align_items_t *ai = style; + + return lxb_css_value_serialize(ai->type, cb, ctx); +} + +/* Align-self. */ + +LXB_API void * +lxb_css_property_align_self_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_align_self_t)); +} + +LXB_API void * +lxb_css_property_align_self_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_align_self_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_align_self_t *as = style; + + return lxb_css_value_serialize(as->type, cb, ctx); +} + +/* Align-content. */ + +LXB_API void * +lxb_css_property_align_content_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_align_content_t)); +} + +LXB_API void * +lxb_css_property_align_content_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_align_content_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_align_content_t *ac = style; + + return lxb_css_value_serialize(ac->type, cb, ctx); +} + +/* Dominant-baseline. */ + +LXB_API void * +lxb_css_property_dominant_baseline_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_dominant_baseline_t)); +} + +LXB_API void * +lxb_css_property_dominant_baseline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_dominant_baseline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_dominant_baseline_t *db = style; + + return lxb_css_value_serialize(db->type, cb, ctx); +} + +/* Vertical-align. */ + +LXB_API void * +lxb_css_property_vertical_align_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_vertical_align_t)); +} + +LXB_API void * +lxb_css_property_vertical_align_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_vertical_align_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool is; + lxb_status_t status; + const lxb_css_property_vertical_align_t *va = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + is = false; + + if (va->type != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(va->type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + is = true; + } + + if (va->alignment.type != LXB_CSS_VALUE__UNDEF) { + if (is) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(va->alignment.type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + is = true; + } + + if (va->shift.type != LXB_CSS_VALUE__UNDEF) { + if (is) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_length_percentage_sr(&va->shift, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + return LXB_STATUS_OK; +} + +/* Baseline-source. */ + +LXB_API void * +lxb_css_property_baseline_source_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_baseline_source_t)); +} + +LXB_API void * +lxb_css_property_baseline_source_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_baseline_source_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_baseline_source_t *bs = style; + + return lxb_css_value_serialize(bs->type, cb, ctx); +} + +/* Alignment-baseline. */ + +LXB_API void * +lxb_css_property_alignment_baseline_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_alignment_baseline_t)); +} + +LXB_API void * +lxb_css_property_alignment_baseline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_alignment_baseline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_alignment_baseline_t *ab = style; + + return lxb_css_value_serialize(ab->type, cb, ctx); +} + +/* Baseline-shift. */ + +LXB_API void * +lxb_css_property_baseline_shift_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_baseline_shift_t)); +} + +LXB_API void * +lxb_css_property_baseline_shift_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_baseline_shift_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_length_percentage_sr(style, cb, ctx); +} + +/* Line-height. */ + +LXB_API void * +lxb_css_property_line_height_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_line_height_t)); +} + +LXB_API void * +lxb_css_property_line_height_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_line_height_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_number_length_percentage_type_sr(style, cb, ctx); +} + +/* Z-index. */ + +LXB_API void * +lxb_css_property_z_index_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_property_z_index_t)); +} + +LXB_API void * +lxb_css_property_z_index_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_z_index_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_value_integer_type_sr(style, cb, ctx); +} + +/* Direction. */ + +LXB_API void * +lxb_css_property_direction_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_direction_t)); +} + +LXB_API void * +lxb_css_property_direction_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_direction_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_direction_t *dr = style; + + return lxb_css_value_serialize(dr->type, cb, ctx); +} + +/* Unicode-bidi. */ + +LXB_API void * +lxb_css_property_unicode_bidi_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_unicode_bidi_t)); +} + +LXB_API void * +lxb_css_property_unicode_bidi_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_unicode_bidi_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_unicode_bidi_t *ub = style; + + return lxb_css_value_serialize(ub->type, cb, ctx); +} + +/* Writing-mode. */ + +LXB_API void * +lxb_css_property_writing_mode_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_writing_mode_t)); +} + +LXB_API void * +lxb_css_property_writing_mode_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_writing_mode_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_writing_mode_t *wm = style; + + return lxb_css_value_serialize(wm->type, cb, ctx); +} + +/* Text-orientation. */ + +LXB_API void * +lxb_css_property_text_orientation_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_orientation_t)); +} + +LXB_API void * +lxb_css_property_text_orientation_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_orientation_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_orientation_t *to = style; + + return lxb_css_value_serialize(to->type, cb, ctx); +} + +/* Text-combine-upright. */ + +LXB_API void * +lxb_css_property_text_combine_upright_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_combine_upright_t)); +} + +LXB_API void * +lxb_css_property_text_combine_upright_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_combine_upright_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_property_text_combine_upright_t *tcu = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + if (tcu->type == LXB_CSS_TEXT_COMBINE_UPRIGHT_DIGITS) { + status = lxb_css_value_serialize(tcu->type, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (tcu->digits.num != 0) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + + return lxb_css_value_integer_sr(&tcu->digits, cb, ctx); + } + + return LXB_STATUS_OK; + } + + return lxb_css_value_serialize(tcu->type, cb, ctx); +} + +/* Overflow-x. */ + +LXB_API void * +lxb_css_property_overflow_x_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_overflow_x_t)); +} + +LXB_API void * +lxb_css_property_overflow_x_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_overflow_x_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_overflow_x_t *ox = style; + + return lxb_css_value_serialize(ox->type, cb, ctx); +} + +/* Overflow-y. */ + +LXB_API void * +lxb_css_property_overflow_y_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_overflow_y_t)); +} + +LXB_API void * +lxb_css_property_overflow_y_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_overflow_y_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_overflow_y_t *oy = style; + + return lxb_css_value_serialize(oy->type, cb, ctx); +} + +/* Overflow-block. */ + +LXB_API void * +lxb_css_property_overflow_block_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_overflow_block_t)); +} + +LXB_API void * +lxb_css_property_overflow_block_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_overflow_block_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_overflow_block_t *ob = style; + + return lxb_css_value_serialize(ob->type, cb, ctx); +} + +/* Overflow-inline. */ + +LXB_API void * +lxb_css_property_overflow_inline_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_overflow_inline_t)); +} + +LXB_API void * +lxb_css_property_overflow_inline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_overflow_inline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_overflow_inline_t *oi = style; + + return lxb_css_value_serialize(oi->type, cb, ctx); +} + +/* Text-overflow. */ + +LXB_API void * +lxb_css_property_text_overflow_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_overflow_t)); +} + +LXB_API void * +lxb_css_property_text_overflow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_overflow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_overflow_t *to = style; + + return lxb_css_value_serialize(to->type, cb, ctx); +} + +/* Text-decoration-line. */ + +LXB_API void * +lxb_css_property_text_decoration_line_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_decoration_line_t)); +} + +LXB_API void * +lxb_css_property_text_decoration_line_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_decoration_line_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool itis; + lxb_status_t status; + const lxb_css_property_text_decoration_line_t *tdl = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + itis = false; + + if (tdl->underline != LXB_CSS_VALUE__UNDEF) { + status = lxb_css_value_serialize(tdl->underline, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + itis = true; + } + + if (tdl->overline != LXB_CSS_VALUE__UNDEF) { + if (itis) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(tdl->overline, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + itis = true; + } + + if (tdl->line_through != LXB_CSS_VALUE__UNDEF) { + if (itis) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_value_serialize(tdl->line_through, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + itis = true; + } + + if (tdl->blink != LXB_CSS_VALUE__UNDEF) { + if (itis) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_value_serialize(tdl->blink, cb, ctx); + } + + if (itis) { + return LXB_STATUS_OK; + } + + return lxb_css_value_serialize(tdl->type, cb, ctx); +} + +/* Text-decoration-style. */ + +LXB_API void * +lxb_css_property_text_decoration_style_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_decoration_style_t)); +} + +LXB_API void * +lxb_css_property_text_decoration_style_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_decoration_style_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_property_text_decoration_style_t *tds = style; + + return lxb_css_value_serialize(tds->type, cb, ctx); +} + +/* Text-decoration-color. */ + +LXB_API void * +lxb_css_property_text_decoration_color_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_decoration_color_t)); +} + +LXB_API void * +lxb_css_property_text_decoration_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_decoration_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_color_serialize(style, cb, ctx); +} + +/* Text-decoration. */ + +LXB_API void * +lxb_css_property_text_decoration_create(lxb_css_memory_t *memory) +{ + return lexbor_mraw_calloc(memory->mraw, + sizeof(lxb_css_property_text_decoration_t)); +} + +LXB_API void * +lxb_css_property_text_decoration_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy) +{ + return lxb_css_property__undef_destroy(memory, style, self_destroy); +} + +LXB_API lxb_status_t +lxb_css_property_text_decoration_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + bool itis; + lxb_status_t status; + const lxb_css_property_text_decoration_t *td = style; + + static const lexbor_str_t str_ws = lexbor_str(" "); + + itis = false; + + if (td->line.type != LXB_CSS_VALUE__UNDEF + || td->line.underline != LXB_CSS_VALUE__UNDEF + || td->line.overline != LXB_CSS_VALUE__UNDEF + || td->line.line_through != LXB_CSS_VALUE__UNDEF + || td->line.blink != LXB_CSS_VALUE__UNDEF) + { + status = lxb_css_property_text_decoration_line_serialize(&td->line, + cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + itis = true; + } + + if (td->style.type != LXB_CSS_VALUE__UNDEF) { + if (itis) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + status = lxb_css_property_text_decoration_style_serialize(&td->style, + cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + itis = true; + } + + if (td->color.type != LXB_CSS_VALUE__UNDEF) { + if (itis) { + lexbor_serialize_write(cb, str_ws.data, str_ws.length, ctx, status); + } + + return lxb_css_property_text_decoration_color_serialize(&td->color, + cb, ctx); + } + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.h new file mode 100644 index 0000000..fb02c2e --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property.h @@ -0,0 +1,1754 @@ +/* + * Copyright (C) 2021-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_PROPERTY_H +#define LXB_CSS_PROPERTY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/serialize.h" +#include "lexbor/core/array_obj.h" +#include "lexbor/css/base.h" +#include "lexbor/css/value.h" +#include "lexbor/css/unit/const.h" +#include "lexbor/css/property/const.h" + + +typedef struct { + lxb_css_property_type_t type; + lexbor_str_t value; +} +lxb_css_property__undef_t; + +typedef struct { + lexbor_str_t name; + lexbor_str_t value; +} +lxb_css_property__custom_t; + +typedef struct { + lxb_css_display_type_t a; + lxb_css_display_type_t b; + lxb_css_display_type_t c; +} +lxb_css_property_display_t; + +typedef lxb_css_value_integer_type_t lxb_css_property_order_t; + +typedef struct { + lxb_css_visibility_type_t type; +} +lxb_css_property_visibility_t; + +typedef lxb_css_value_length_percentage_t lxb_css_property_width_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_height_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_min_width_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_min_height_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_max_width_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_max_height_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_margin_top_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_margin_right_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_margin_bottom_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_margin_left_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_padding_top_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_padding_right_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_padding_bottom_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_padding_left_t; + +typedef struct { + lxb_css_box_sizing_type_t type; +} +lxb_css_property_box_sizing_t; + +typedef struct { + lxb_css_property_margin_top_t top; + lxb_css_property_margin_right_t right; + lxb_css_property_margin_bottom_t bottom; + lxb_css_property_margin_left_t left; +} +lxb_css_property_margin_t; + +typedef struct { + lxb_css_property_padding_top_t top; + lxb_css_property_padding_right_t right; + lxb_css_property_padding_bottom_t bottom; + lxb_css_property_padding_left_t left; +} +lxb_css_property_padding_t; + +typedef struct { + lxb_css_value_type_t style; + lxb_css_value_length_type_t width; + lxb_css_value_color_t color; +} +lxb_css_property_border_t; + +typedef lxb_css_property_border_t lxb_css_property_border_top_t; +typedef lxb_css_property_border_t lxb_css_property_border_right_t; +typedef lxb_css_property_border_t lxb_css_property_border_bottom_t; +typedef lxb_css_property_border_t lxb_css_property_border_left_t; + +typedef lxb_css_value_color_t lxb_css_property_border_top_color_t; +typedef lxb_css_value_color_t lxb_css_property_border_right_color_t; +typedef lxb_css_value_color_t lxb_css_property_border_bottom_color_t; +typedef lxb_css_value_color_t lxb_css_property_border_left_color_t; + +typedef lxb_css_value_color_t lxb_css_property_background_color_t; + +typedef lxb_css_value_color_t lxb_css_property_color_t; +typedef lxb_css_value_number_percentage_t lxb_css_property_opacity_t; + +typedef struct { + lxb_css_position_type_t type; +} +lxb_css_property_position_t; + +typedef lxb_css_value_length_percentage_t lxb_css_property_top_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_right_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_bottom_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_left_t; + +typedef lxb_css_value_length_percentage_t lxb_css_property_inset_block_start_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_inset_inline_start_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_inset_block_end_t; +typedef lxb_css_value_length_percentage_t lxb_css_property_inset_inline_end_t; + +typedef struct { + lxb_css_text_transform_type_t type_case; + lxb_css_text_transform_type_t full_width; + lxb_css_text_transform_type_t full_size_kana; +} +lxb_css_property_text_transform_t; + +typedef struct { + lxb_css_text_align_type_t type; +} +lxb_css_property_text_align_t; + +typedef struct { + lxb_css_text_align_all_type_t type; +} +lxb_css_property_text_align_all_t; + +typedef struct { + lxb_css_text_align_last_type_t type; +} +lxb_css_property_text_align_last_t; + +typedef struct { + lxb_css_text_justify_type_t type; +} +lxb_css_property_text_justify_t; + +typedef struct { + lxb_css_value_length_percentage_t length; + lxb_css_text_indent_type_t type; + lxb_css_text_indent_type_t hanging; + lxb_css_text_indent_type_t each_line; +} +lxb_css_property_text_indent_t; + +typedef struct { + lxb_css_white_space_type_t type; +} +lxb_css_property_white_space_t; + +typedef lxb_css_value_number_length_t lxb_css_property_tab_size_t; + +typedef struct { + lxb_css_word_break_type_t type; +} +lxb_css_property_word_break_t; + +typedef struct { + lxb_css_line_break_type_t type; +} +lxb_css_property_line_break_t; + +typedef struct { + lxb_css_hyphens_type_t type; +} +lxb_css_property_hyphens_t; + +typedef struct { + lxb_css_overflow_wrap_type_t type; +} +lxb_css_property_overflow_wrap_t; + +typedef struct { + lxb_css_word_wrap_type_t type; +} +lxb_css_property_word_wrap_t; + +typedef lxb_css_value_length_type_t lxb_css_property_word_spacing_t; +typedef lxb_css_value_length_type_t lxb_css_property_letter_spacing_t; + +typedef struct { + lxb_css_hanging_punctuation_type_t type_first; + lxb_css_hanging_punctuation_type_t force_allow; + lxb_css_hanging_punctuation_type_t last; +} +lxb_css_property_hanging_punctuation_t; + +typedef struct lxb_css_property_family_name lxb_css_property_family_name_t; + +struct lxb_css_property_family_name { + bool generic; + + union { + lxb_css_font_family_type_t type; + lexbor_str_t str; + } u; + + lxb_css_property_family_name_t *next; + lxb_css_property_family_name_t *prev; +}; + +typedef struct { + lxb_css_property_family_name_t *first; + lxb_css_property_family_name_t *last; + + size_t count; +} +lxb_css_property_font_family_t; + +typedef lxb_css_value_number_type_t lxb_css_property_font_weight_t; +typedef lxb_css_value_percentage_type_t lxb_css_property_font_stretch_t; +typedef lxb_css_value_angle_type_t lxb_css_property_font_style_t; +typedef lxb_css_value_length_percentage_type_t lxb_css_property_font_size_t; + +typedef struct { + lxb_css_float_reference_type_t type; +} +lxb_css_property_float_reference_t; + +typedef struct { + lxb_css_float_type_t type; + lxb_css_value_length_type_t length; + lxb_css_float_type_t snap_type; +} +lxb_css_property_float_t; + +typedef struct { + lxb_css_clear_type_t type; +} +lxb_css_property_clear_t; + +typedef lxb_css_value_length_percentage_t lxb_css_property_float_offset_t; +typedef lxb_css_value_integer_type_t lxb_css_property_float_defer_t; + +typedef struct { + lxb_css_wrap_flow_type_t type; +} +lxb_css_property_wrap_flow_t; + +typedef struct { + lxb_css_wrap_through_type_t type; +} +lxb_css_property_wrap_through_t; + +typedef struct { + lxb_css_flex_direction_type_t type; +} +lxb_css_property_flex_direction_t; + +typedef struct { + lxb_css_flex_wrap_type_t type; +} +lxb_css_property_flex_wrap_t; + +typedef struct { + lxb_css_flex_direction_type_t type_direction; + lxb_css_flex_wrap_type_t wrap; +} +lxb_css_property_flex_flow_t; + +typedef lxb_css_value_number_type_t lxb_css_property_flex_grow_t; +typedef lxb_css_value_number_type_t lxb_css_property_flex_shrink_t; +typedef lxb_css_property_width_t lxb_css_property_flex_basis_t; + +typedef struct { + lxb_css_flex_type_t type; + lxb_css_property_flex_grow_t grow; + lxb_css_property_flex_shrink_t shrink; + lxb_css_property_flex_basis_t basis; +} +lxb_css_property_flex_t; + +typedef struct { + lxb_css_justify_content_type_t type; +} +lxb_css_property_justify_content_t; + +typedef struct { + lxb_css_align_items_type_t type; +} +lxb_css_property_align_items_t; + +typedef struct { + lxb_css_align_self_type_t type; +} +lxb_css_property_align_self_t; + +typedef struct { + lxb_css_align_content_type_t type; +} +lxb_css_property_align_content_t; + +typedef struct { + lxb_css_dominant_baseline_type_t type; +} +lxb_css_property_dominant_baseline_t; + +typedef struct { + lxb_css_baseline_source_type_t type; +} +lxb_css_property_baseline_source_t; + +typedef struct { + lxb_css_alignment_baseline_type_t type; +} +lxb_css_property_alignment_baseline_t; + +typedef lxb_css_value_length_percentage_t lxb_css_property_baseline_shift_t; + +typedef struct { + lxb_css_vertical_align_type_t type; + lxb_css_property_alignment_baseline_t alignment; + lxb_css_property_baseline_shift_t shift; +} +lxb_css_property_vertical_align_t; + +typedef lxb_css_value_number_length_percentage_t lxb_css_property_line_height_t; + +typedef lxb_css_value_integer_type_t lxb_css_property_z_index_t; + +typedef struct { + lxb_css_direction_type_t type; +} +lxb_css_property_direction_t; + +typedef struct { + lxb_css_unicode_bidi_type_t type; +} +lxb_css_property_unicode_bidi_t; + +typedef struct { + lxb_css_writing_mode_type_t type; +} +lxb_css_property_writing_mode_t; + +typedef struct { + lxb_css_text_orientation_type_t type; +} +lxb_css_property_text_orientation_t; + +typedef struct { + lxb_css_text_combine_upright_type_t type; + /* + * If the integer is omitted, it computes to 2. + * Integers outside the range 2-4 are invalid. + */ + lxb_css_value_integer_t digits; +} +lxb_css_property_text_combine_upright_t; + +typedef struct { + lxb_css_overflow_x_type_t type; +} +lxb_css_property_overflow_x_t; + +typedef struct { + lxb_css_overflow_y_type_t type; +} +lxb_css_property_overflow_y_t; + +typedef struct { + lxb_css_overflow_block_type_t type; +} +lxb_css_property_overflow_block_t; + +typedef struct { + lxb_css_overflow_inline_type_t type; +} +lxb_css_property_overflow_inline_t; + +typedef struct { + lxb_css_text_overflow_type_t type; +} +lxb_css_property_text_overflow_t; + +typedef struct { + lxb_css_text_decoration_line_type_t type; + lxb_css_text_decoration_line_type_t underline; + lxb_css_text_decoration_line_type_t overline; + lxb_css_text_decoration_line_type_t line_through; + lxb_css_text_decoration_line_type_t blink; +} +lxb_css_property_text_decoration_line_t; + +typedef struct { + lxb_css_text_decoration_style_type_t type; +} +lxb_css_property_text_decoration_style_t; + +typedef lxb_css_value_color_t lxb_css_property_text_decoration_color_t; + +typedef struct { + lxb_css_property_text_decoration_line_t line; + lxb_css_property_text_decoration_style_t style; + lxb_css_property_text_decoration_color_t color; +} +lxb_css_property_text_decoration_t; + + +LXB_API const lxb_css_entry_data_t * +lxb_css_property_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_entry_data_t * +lxb_css_property_by_id(uintptr_t id); + +LXB_API const void * +lxb_css_property_initial_by_id(uintptr_t id); + +LXB_API void * +lxb_css_property_destroy(lxb_css_memory_t *memory, void *style, + lxb_css_property_type_t type, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_serialize(const void *style, lxb_css_property_type_t type, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_property_serialize_str(const void *style, lxb_css_property_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str); + +LXB_API lxb_status_t +lxb_css_property_serialize_name(const void *style, lxb_css_property_type_t type, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_property_serialize_name_str(const void *style, lxb_css_property_type_t type, + lexbor_mraw_t *mraw, lexbor_str_t *str); + +/* _undef. */ + +LXB_API void * +lxb_css_property__undef_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property__undef_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property__undef_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_property__undef_serialize_name(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_property__undef_serialize_value(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* _custom. */ + +LXB_API void * +lxb_css_property__custom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property__custom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property__custom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); +LXB_API lxb_status_t +lxb_css_property__custom_serialize_name(const void *style, + lexbor_serialize_cb_f cb, void *ctx); +LXB_API lxb_status_t +lxb_css_property__custom_serialize_value(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Display. */ + +LXB_API void * +lxb_css_property_display_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_display_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_display_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Order. */ + +LXB_API void * +lxb_css_property_order_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_order_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_order_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Visibility. */ + +LXB_API void * +lxb_css_property_visibility_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_visibility_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_visibility_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Width. */ + +LXB_API void * +lxb_css_property_width_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_width_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_width_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Height. */ + +LXB_API void * +lxb_css_property_height_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_height_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_height_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Box-sizing. */ + +LXB_API void * +lxb_css_property_box_sizing_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_box_sizing_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_box_sizing_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Min-width. */ + +LXB_API void * +lxb_css_property_min_width_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_min_width_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_min_width_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Min-height. */ + +LXB_API void * +lxb_css_property_min_height_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_min_height_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_min_height_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Max-width. */ + +LXB_API void * +lxb_css_property_max_width_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_max_width_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_max_width_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Max-height. */ + +LXB_API void * +lxb_css_property_max_height_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_max_height_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_max_height_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Margin. */ + +LXB_API void * +lxb_css_property_margin_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_margin_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_margin_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Margin-top. */ + +LXB_API void * +lxb_css_property_margin_top_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_margin_top_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_margin_top_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Margin-right. */ + +LXB_API void * +lxb_css_property_margin_right_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_margin_right_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_margin_right_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Margin-bottom. */ + +LXB_API void * +lxb_css_property_margin_bottom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_margin_bottom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_margin_bottom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Margin-left. */ + +LXB_API void * +lxb_css_property_margin_left_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_margin_left_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_margin_left_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Padding. */ + +LXB_API void * +lxb_css_property_padding_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_padding_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_padding_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Padding-top. */ + +LXB_API void * +lxb_css_property_padding_top_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_padding_top_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_padding_top_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Padding-right. */ + +LXB_API void * +lxb_css_property_padding_right_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_padding_right_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_padding_right_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Padding-bottom. */ + +LXB_API void * +lxb_css_property_padding_bottom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_padding_bottom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_padding_bottom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Padding-left. */ + +LXB_API void * +lxb_css_property_padding_left_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_padding_left_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_padding_left_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border. */ + +LXB_API void * +lxb_css_property_border_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-top. */ + +LXB_API void * +lxb_css_property_border_top_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_top_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_top_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-right. */ + +LXB_API void * +lxb_css_property_border_right_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_right_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_right_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-bottom. */ + +LXB_API void * +lxb_css_property_border_bottom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_bottom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_bottom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-left. */ + +LXB_API void * +lxb_css_property_border_left_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_left_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_left_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-top-color. */ + +LXB_API void * +lxb_css_property_border_top_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_top_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_top_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-right-color. */ + +LXB_API void * +lxb_css_property_border_right_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_right_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_right_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-bottom-color. */ + +LXB_API void * +lxb_css_property_border_bottom_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_bottom_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_bottom_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Border-left-color. */ + +LXB_API void * +lxb_css_property_border_left_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_border_left_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_border_left_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Background-color. */ + +LXB_API void * +lxb_css_property_background_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_background_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_background_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Color. */ + +LXB_API void * +lxb_css_property_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Opacity. */ + +LXB_API void * +lxb_css_property_opacity_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_opacity_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_opacity_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Position. */ + +LXB_API void * +lxb_css_property_position_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_position_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_position_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Top. */ + +LXB_API void * +lxb_css_property_top_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_top_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_top_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Right. */ + +LXB_API void * +lxb_css_property_right_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_right_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_right_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Bottom. */ + +LXB_API void * +lxb_css_property_bottom_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_bottom_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_bottom_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Left. */ + +LXB_API void * +lxb_css_property_left_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_left_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_left_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Inset-block-start. */ + +LXB_API void * +lxb_css_property_inset_block_start_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_inset_block_start_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_inset_block_start_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Inset-inline-start. */ + +LXB_API void * +lxb_css_property_inset_inline_start_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_inset_inline_start_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_inset_inline_start_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Inset-block-end. */ + +LXB_API void * +lxb_css_property_inset_block_end_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_inset_block_end_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_inset_block_end_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Inset-inline-end. */ + +LXB_API void * +lxb_css_property_inset_inline_end_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_inset_inline_end_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); +LXB_API lxb_status_t +lxb_css_property_inset_inline_end_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-transform. */ + +LXB_API void * +lxb_css_property_text_transform_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_transform_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_transform_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-align. */ + +LXB_API void * +lxb_css_property_text_align_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_align_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_align_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-align-all. */ + +LXB_API void * +lxb_css_property_text_align_all_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_align_all_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_align_all_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-align-last. */ + +LXB_API void * +lxb_css_property_text_align_last_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_align_last_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_align_last_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-justify. */ + +LXB_API void * +lxb_css_property_text_justify_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_justify_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_justify_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-indent. */ + +LXB_API void * +lxb_css_property_text_indent_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_indent_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_indent_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* White-space. */ + +LXB_API void * +lxb_css_property_white_space_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_white_space_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_white_space_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Tab-size. */ + +LXB_API void * +lxb_css_property_tab_size_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_tab_size_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_tab_size_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Word-break. */ + +LXB_API void * +lxb_css_property_word_break_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_word_break_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_word_break_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Line-break. */ + +LXB_API void * +lxb_css_property_line_break_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_line_break_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_line_break_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Hyphens. */ + +LXB_API void * +lxb_css_property_hyphens_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_hyphens_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_hyphens_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Overflow-wrap. */ + +LXB_API void * +lxb_css_property_overflow_wrap_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_overflow_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_overflow_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Word-wrap. */ + +LXB_API void * +lxb_css_property_word_wrap_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_word_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_word_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Word-spacing. */ + +LXB_API void * +lxb_css_property_word_spacing_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_word_spacing_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_word_spacing_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Letter-spacing. */ + +LXB_API void * +lxb_css_property_letter_spacing_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_letter_spacing_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_letter_spacing_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Hanging-punctuation. */ + +LXB_API void * +lxb_css_property_hanging_punctuation_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_hanging_punctuation_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_hanging_punctuation_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Font-family. */ + +LXB_API void * +lxb_css_property_font_family_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_font_family_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_font_family_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Font-weight. */ + +LXB_API void * +lxb_css_property_font_weight_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_font_weight_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_font_weight_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Font-stretch. */ + +LXB_API void * +lxb_css_property_font_stretch_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_font_stretch_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_font_stretch_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Font-style. */ + +LXB_API void * +lxb_css_property_font_style_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_font_style_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_font_style_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Font-size. */ + +LXB_API void * +lxb_css_property_font_size_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_font_size_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_font_size_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Float-reference. */ + +LXB_API void * +lxb_css_property_float_reference_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_float_reference_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_float_reference_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Float. */ + +LXB_API void * +lxb_css_property_float_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_float_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_float_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Clear. */ + +LXB_API void * +lxb_css_property_clear_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_clear_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_clear_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Float-defer. */ + +LXB_API void * +lxb_css_property_float_defer_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_float_defer_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_float_defer_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Float-offset. */ + +LXB_API void * +lxb_css_property_float_offset_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_float_offset_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_float_offset_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Wrap-flow. */ + +LXB_API void * +lxb_css_property_wrap_flow_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_wrap_flow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_wrap_flow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Wrap-through. */ + +LXB_API void * +lxb_css_property_wrap_through_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_wrap_through_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_wrap_through_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-direction. */ + +LXB_API void * +lxb_css_property_flex_direction_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_direction_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_direction_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-wrap. */ + +LXB_API void * +lxb_css_property_flex_wrap_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_wrap_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_wrap_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-flow. */ + +LXB_API void * +lxb_css_property_flex_flow_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_flow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_flow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex. */ + +LXB_API void * +lxb_css_property_flex_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-grow. */ + +LXB_API void * +lxb_css_property_flex_grow_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_grow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_grow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-shrink. */ + +LXB_API void * +lxb_css_property_flex_shrink_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_shrink_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_shrink_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Flex-basis. */ + +LXB_API void * +lxb_css_property_flex_basis_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_flex_basis_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_flex_basis_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Justify-content. */ + +LXB_API void * +lxb_css_property_justify_content_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_justify_content_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_justify_content_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Align-items. */ + +LXB_API void * +lxb_css_property_align_items_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_align_items_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_align_items_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Align-self. */ + +LXB_API void * +lxb_css_property_align_self_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_align_self_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_align_self_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Align-content. */ + +LXB_API void * +lxb_css_property_align_content_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_align_content_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_align_content_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Dominant-baseline. */ + +LXB_API void * +lxb_css_property_dominant_baseline_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_dominant_baseline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_dominant_baseline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Vertical-align. */ + +LXB_API void * +lxb_css_property_vertical_align_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_vertical_align_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_vertical_align_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Baseline-source. */ + +LXB_API void * +lxb_css_property_baseline_source_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_baseline_source_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_baseline_source_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Alignment-baseline. */ + +LXB_API void * +lxb_css_property_alignment_baseline_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_alignment_baseline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_alignment_baseline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Baseline-shift. */ + +LXB_API void * +lxb_css_property_baseline_shift_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_baseline_shift_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_baseline_shift_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Line-height. */ + +LXB_API void * +lxb_css_property_line_height_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_line_height_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_line_height_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Z-index. */ + +LXB_API void * +lxb_css_property_z_index_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_z_index_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_z_index_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Direction. */ + +LXB_API void * +lxb_css_property_direction_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_direction_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_direction_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Unicode-bidi. */ + +LXB_API void * +lxb_css_property_unicode_bidi_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_unicode_bidi_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_unicode_bidi_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Writing-mode. */ + +LXB_API void * +lxb_css_property_writing_mode_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_writing_mode_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_writing_mode_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-orientation. */ + +LXB_API void * +lxb_css_property_text_orientation_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_orientation_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_orientation_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-combine-upright. */ + +LXB_API void * +lxb_css_property_text_combine_upright_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_combine_upright_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_combine_upright_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Overflow-x. */ + +LXB_API void * +lxb_css_property_overflow_x_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_overflow_x_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_overflow_x_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Overflow-y. */ + +LXB_API void * +lxb_css_property_overflow_y_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_overflow_y_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_overflow_y_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Overflow-block. */ + +LXB_API void * +lxb_css_property_overflow_block_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_overflow_block_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_overflow_block_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Overflow-inline. */ + +LXB_API void * +lxb_css_property_overflow_inline_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_overflow_inline_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_overflow_inline_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-overflow. */ + +LXB_API void * +lxb_css_property_text_overflow_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_overflow_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_overflow_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-decoration-line. */ + +LXB_API void * +lxb_css_property_text_decoration_line_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_decoration_line_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_decoration_line_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-decoration-style. */ + +LXB_API void * +lxb_css_property_text_decoration_style_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_decoration_style_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_decoration_style_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-decoration-color. */ + +LXB_API void * +lxb_css_property_text_decoration_color_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_decoration_color_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_decoration_color_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + +/* Text-decoration. */ + +LXB_API void * +lxb_css_property_text_decoration_create(lxb_css_memory_t *memory); + +LXB_API void * +lxb_css_property_text_decoration_destroy(lxb_css_memory_t *memory, + void *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_property_text_decoration_serialize(const void *style, + lexbor_serialize_cb_f cb, void *ctx); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_PROPERTY_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/const.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/const.h new file mode 100644 index 0000000..4f90bc1 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/const.h @@ -0,0 +1,1180 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_PROPERTY_CONST_H +#define LXB_CSS_PROPERTY_CONST_H + + +#include "lexbor/css/value/const.h" + + +enum { + LXB_CSS_PROPERTY__UNDEF = 0x0000, + LXB_CSS_PROPERTY__CUSTOM = 0x0001, + LXB_CSS_PROPERTY_ALIGN_CONTENT = 0x0002, + LXB_CSS_PROPERTY_ALIGN_ITEMS = 0x0003, + LXB_CSS_PROPERTY_ALIGN_SELF = 0x0004, + LXB_CSS_PROPERTY_ALIGNMENT_BASELINE = 0x0005, + LXB_CSS_PROPERTY_BACKGROUND_COLOR = 0x0006, + LXB_CSS_PROPERTY_BASELINE_SHIFT = 0x0007, + LXB_CSS_PROPERTY_BASELINE_SOURCE = 0x0008, + LXB_CSS_PROPERTY_BORDER = 0x0009, + LXB_CSS_PROPERTY_BORDER_BOTTOM = 0x000a, + LXB_CSS_PROPERTY_BORDER_BOTTOM_COLOR = 0x000b, + LXB_CSS_PROPERTY_BORDER_LEFT = 0x000c, + LXB_CSS_PROPERTY_BORDER_LEFT_COLOR = 0x000d, + LXB_CSS_PROPERTY_BORDER_RIGHT = 0x000e, + LXB_CSS_PROPERTY_BORDER_RIGHT_COLOR = 0x000f, + LXB_CSS_PROPERTY_BORDER_TOP = 0x0010, + LXB_CSS_PROPERTY_BORDER_TOP_COLOR = 0x0011, + LXB_CSS_PROPERTY_BOTTOM = 0x0012, + LXB_CSS_PROPERTY_BOX_SIZING = 0x0013, + LXB_CSS_PROPERTY_CLEAR = 0x0014, + LXB_CSS_PROPERTY_COLOR = 0x0015, + LXB_CSS_PROPERTY_DIRECTION = 0x0016, + LXB_CSS_PROPERTY_DISPLAY = 0x0017, + LXB_CSS_PROPERTY_DOMINANT_BASELINE = 0x0018, + LXB_CSS_PROPERTY_FLEX = 0x0019, + LXB_CSS_PROPERTY_FLEX_BASIS = 0x001a, + LXB_CSS_PROPERTY_FLEX_DIRECTION = 0x001b, + LXB_CSS_PROPERTY_FLEX_FLOW = 0x001c, + LXB_CSS_PROPERTY_FLEX_GROW = 0x001d, + LXB_CSS_PROPERTY_FLEX_SHRINK = 0x001e, + LXB_CSS_PROPERTY_FLEX_WRAP = 0x001f, + LXB_CSS_PROPERTY_FLOAT = 0x0020, + LXB_CSS_PROPERTY_FLOAT_DEFER = 0x0021, + LXB_CSS_PROPERTY_FLOAT_OFFSET = 0x0022, + LXB_CSS_PROPERTY_FLOAT_REFERENCE = 0x0023, + LXB_CSS_PROPERTY_FONT_FAMILY = 0x0024, + LXB_CSS_PROPERTY_FONT_SIZE = 0x0025, + LXB_CSS_PROPERTY_FONT_STRETCH = 0x0026, + LXB_CSS_PROPERTY_FONT_STYLE = 0x0027, + LXB_CSS_PROPERTY_FONT_WEIGHT = 0x0028, + LXB_CSS_PROPERTY_HANGING_PUNCTUATION = 0x0029, + LXB_CSS_PROPERTY_HEIGHT = 0x002a, + LXB_CSS_PROPERTY_HYPHENS = 0x002b, + LXB_CSS_PROPERTY_INSET_BLOCK_END = 0x002c, + LXB_CSS_PROPERTY_INSET_BLOCK_START = 0x002d, + LXB_CSS_PROPERTY_INSET_INLINE_END = 0x002e, + LXB_CSS_PROPERTY_INSET_INLINE_START = 0x002f, + LXB_CSS_PROPERTY_JUSTIFY_CONTENT = 0x0030, + LXB_CSS_PROPERTY_LEFT = 0x0031, + LXB_CSS_PROPERTY_LETTER_SPACING = 0x0032, + LXB_CSS_PROPERTY_LINE_BREAK = 0x0033, + LXB_CSS_PROPERTY_LINE_HEIGHT = 0x0034, + LXB_CSS_PROPERTY_MARGIN = 0x0035, + LXB_CSS_PROPERTY_MARGIN_BOTTOM = 0x0036, + LXB_CSS_PROPERTY_MARGIN_LEFT = 0x0037, + LXB_CSS_PROPERTY_MARGIN_RIGHT = 0x0038, + LXB_CSS_PROPERTY_MARGIN_TOP = 0x0039, + LXB_CSS_PROPERTY_MAX_HEIGHT = 0x003a, + LXB_CSS_PROPERTY_MAX_WIDTH = 0x003b, + LXB_CSS_PROPERTY_MIN_HEIGHT = 0x003c, + LXB_CSS_PROPERTY_MIN_WIDTH = 0x003d, + LXB_CSS_PROPERTY_OPACITY = 0x003e, + LXB_CSS_PROPERTY_ORDER = 0x003f, + LXB_CSS_PROPERTY_OVERFLOW_BLOCK = 0x0040, + LXB_CSS_PROPERTY_OVERFLOW_INLINE = 0x0041, + LXB_CSS_PROPERTY_OVERFLOW_WRAP = 0x0042, + LXB_CSS_PROPERTY_OVERFLOW_X = 0x0043, + LXB_CSS_PROPERTY_OVERFLOW_Y = 0x0044, + LXB_CSS_PROPERTY_PADDING = 0x0045, + LXB_CSS_PROPERTY_PADDING_BOTTOM = 0x0046, + LXB_CSS_PROPERTY_PADDING_LEFT = 0x0047, + LXB_CSS_PROPERTY_PADDING_RIGHT = 0x0048, + LXB_CSS_PROPERTY_PADDING_TOP = 0x0049, + LXB_CSS_PROPERTY_POSITION = 0x004a, + LXB_CSS_PROPERTY_RIGHT = 0x004b, + LXB_CSS_PROPERTY_TAB_SIZE = 0x004c, + LXB_CSS_PROPERTY_TEXT_ALIGN = 0x004d, + LXB_CSS_PROPERTY_TEXT_ALIGN_ALL = 0x004e, + LXB_CSS_PROPERTY_TEXT_ALIGN_LAST = 0x004f, + LXB_CSS_PROPERTY_TEXT_COMBINE_UPRIGHT = 0x0050, + LXB_CSS_PROPERTY_TEXT_DECORATION = 0x0051, + LXB_CSS_PROPERTY_TEXT_DECORATION_COLOR = 0x0052, + LXB_CSS_PROPERTY_TEXT_DECORATION_LINE = 0x0053, + LXB_CSS_PROPERTY_TEXT_DECORATION_STYLE = 0x0054, + LXB_CSS_PROPERTY_TEXT_INDENT = 0x0055, + LXB_CSS_PROPERTY_TEXT_JUSTIFY = 0x0056, + LXB_CSS_PROPERTY_TEXT_ORIENTATION = 0x0057, + LXB_CSS_PROPERTY_TEXT_OVERFLOW = 0x0058, + LXB_CSS_PROPERTY_TEXT_TRANSFORM = 0x0059, + LXB_CSS_PROPERTY_TOP = 0x005a, + LXB_CSS_PROPERTY_UNICODE_BIDI = 0x005b, + LXB_CSS_PROPERTY_VERTICAL_ALIGN = 0x005c, + LXB_CSS_PROPERTY_VISIBILITY = 0x005d, + LXB_CSS_PROPERTY_WHITE_SPACE = 0x005e, + LXB_CSS_PROPERTY_WIDTH = 0x005f, + LXB_CSS_PROPERTY_WORD_BREAK = 0x0060, + LXB_CSS_PROPERTY_WORD_SPACING = 0x0061, + LXB_CSS_PROPERTY_WORD_WRAP = 0x0062, + LXB_CSS_PROPERTY_WRAP_FLOW = 0x0063, + LXB_CSS_PROPERTY_WRAP_THROUGH = 0x0064, + LXB_CSS_PROPERTY_WRITING_MODE = 0x0065, + LXB_CSS_PROPERTY_Z_INDEX = 0x0066, + LXB_CSS_PROPERTY__LAST_ENTRY = 0x0067 +}; +typedef uintptr_t lxb_css_property_type_t; + +enum { + LXB_CSS_ALIGN_CONTENT_FLEX_START = LXB_CSS_VALUE_FLEX_START, + LXB_CSS_ALIGN_CONTENT_FLEX_END = LXB_CSS_VALUE_FLEX_END, + LXB_CSS_ALIGN_CONTENT_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_ALIGN_CONTENT_SPACE_BETWEEN = LXB_CSS_VALUE_SPACE_BETWEEN, + LXB_CSS_ALIGN_CONTENT_SPACE_AROUND = LXB_CSS_VALUE_SPACE_AROUND, + LXB_CSS_ALIGN_CONTENT_STRETCH = LXB_CSS_VALUE_STRETCH +}; +typedef unsigned int lxb_css_align_content_type_t; + +enum { + LXB_CSS_ALIGN_ITEMS_FLEX_START = LXB_CSS_VALUE_FLEX_START, + LXB_CSS_ALIGN_ITEMS_FLEX_END = LXB_CSS_VALUE_FLEX_END, + LXB_CSS_ALIGN_ITEMS_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_ALIGN_ITEMS_BASELINE = LXB_CSS_VALUE_BASELINE, + LXB_CSS_ALIGN_ITEMS_STRETCH = LXB_CSS_VALUE_STRETCH +}; +typedef unsigned int lxb_css_align_items_type_t; + +enum { + LXB_CSS_ALIGN_SELF_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_ALIGN_SELF_FLEX_START = LXB_CSS_VALUE_FLEX_START, + LXB_CSS_ALIGN_SELF_FLEX_END = LXB_CSS_VALUE_FLEX_END, + LXB_CSS_ALIGN_SELF_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_ALIGN_SELF_BASELINE = LXB_CSS_VALUE_BASELINE, + LXB_CSS_ALIGN_SELF_STRETCH = LXB_CSS_VALUE_STRETCH +}; +typedef unsigned int lxb_css_align_self_type_t; + +enum { + LXB_CSS_ALIGNMENT_BASELINE_BASELINE = LXB_CSS_VALUE_BASELINE, + LXB_CSS_ALIGNMENT_BASELINE_TEXT_BOTTOM = LXB_CSS_VALUE_TEXT_BOTTOM, + LXB_CSS_ALIGNMENT_BASELINE_ALPHABETIC = LXB_CSS_VALUE_ALPHABETIC, + LXB_CSS_ALIGNMENT_BASELINE_IDEOGRAPHIC = LXB_CSS_VALUE_IDEOGRAPHIC, + LXB_CSS_ALIGNMENT_BASELINE_MIDDLE = LXB_CSS_VALUE_MIDDLE, + LXB_CSS_ALIGNMENT_BASELINE_CENTRAL = LXB_CSS_VALUE_CENTRAL, + LXB_CSS_ALIGNMENT_BASELINE_MATHEMATICAL = LXB_CSS_VALUE_MATHEMATICAL, + LXB_CSS_ALIGNMENT_BASELINE_TEXT_TOP = LXB_CSS_VALUE_TEXT_TOP +}; +typedef unsigned int lxb_css_alignment_baseline_type_t; + + + +enum { + LXB_CSS_BASELINE_SHIFT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_BASELINE_SHIFT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_BASELINE_SHIFT_SUB = LXB_CSS_VALUE_SUB, + LXB_CSS_BASELINE_SHIFT_SUPER = LXB_CSS_VALUE_SUPER, + LXB_CSS_BASELINE_SHIFT_TOP = LXB_CSS_VALUE_TOP, + LXB_CSS_BASELINE_SHIFT_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_BASELINE_SHIFT_BOTTOM = LXB_CSS_VALUE_BOTTOM +}; +typedef unsigned int lxb_css_baseline_shift_type_t; + +enum { + LXB_CSS_BASELINE_SOURCE_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_BASELINE_SOURCE_FIRST = LXB_CSS_VALUE_FIRST, + LXB_CSS_BASELINE_SOURCE_LAST = LXB_CSS_VALUE_LAST +}; +typedef unsigned int lxb_css_baseline_source_type_t; + +enum { + LXB_CSS_BORDER_THIN = LXB_CSS_VALUE_THIN, + LXB_CSS_BORDER_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_BORDER_THICK = LXB_CSS_VALUE_THICK, + LXB_CSS_BORDER_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_BORDER_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_BORDER_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_BORDER_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_BORDER_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_BORDER_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_BORDER_GROOVE = LXB_CSS_VALUE_GROOVE, + LXB_CSS_BORDER_RIDGE = LXB_CSS_VALUE_RIDGE, + LXB_CSS_BORDER_INSET = LXB_CSS_VALUE_INSET, + LXB_CSS_BORDER_OUTSET = LXB_CSS_VALUE_OUTSET, + LXB_CSS_BORDER__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_border_type_t; + +enum { + LXB_CSS_BORDER_BOTTOM_THIN = LXB_CSS_VALUE_THIN, + LXB_CSS_BORDER_BOTTOM_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_BORDER_BOTTOM_THICK = LXB_CSS_VALUE_THICK, + LXB_CSS_BORDER_BOTTOM_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_BORDER_BOTTOM_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_BORDER_BOTTOM_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_BORDER_BOTTOM_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_BORDER_BOTTOM_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_BORDER_BOTTOM_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_BORDER_BOTTOM_GROOVE = LXB_CSS_VALUE_GROOVE, + LXB_CSS_BORDER_BOTTOM_RIDGE = LXB_CSS_VALUE_RIDGE, + LXB_CSS_BORDER_BOTTOM_INSET = LXB_CSS_VALUE_INSET, + LXB_CSS_BORDER_BOTTOM_OUTSET = LXB_CSS_VALUE_OUTSET, + LXB_CSS_BORDER_BOTTOM__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_border_bottom_type_t; + + + +enum { + LXB_CSS_BORDER_LEFT_THIN = LXB_CSS_VALUE_THIN, + LXB_CSS_BORDER_LEFT_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_BORDER_LEFT_THICK = LXB_CSS_VALUE_THICK, + LXB_CSS_BORDER_LEFT_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_BORDER_LEFT_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_BORDER_LEFT_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_BORDER_LEFT_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_BORDER_LEFT_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_BORDER_LEFT_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_BORDER_LEFT_GROOVE = LXB_CSS_VALUE_GROOVE, + LXB_CSS_BORDER_LEFT_RIDGE = LXB_CSS_VALUE_RIDGE, + LXB_CSS_BORDER_LEFT_INSET = LXB_CSS_VALUE_INSET, + LXB_CSS_BORDER_LEFT_OUTSET = LXB_CSS_VALUE_OUTSET, + LXB_CSS_BORDER_LEFT__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_border_left_type_t; + + + +enum { + LXB_CSS_BORDER_RIGHT_THIN = LXB_CSS_VALUE_THIN, + LXB_CSS_BORDER_RIGHT_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_BORDER_RIGHT_THICK = LXB_CSS_VALUE_THICK, + LXB_CSS_BORDER_RIGHT_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_BORDER_RIGHT_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_BORDER_RIGHT_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_BORDER_RIGHT_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_BORDER_RIGHT_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_BORDER_RIGHT_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_BORDER_RIGHT_GROOVE = LXB_CSS_VALUE_GROOVE, + LXB_CSS_BORDER_RIGHT_RIDGE = LXB_CSS_VALUE_RIDGE, + LXB_CSS_BORDER_RIGHT_INSET = LXB_CSS_VALUE_INSET, + LXB_CSS_BORDER_RIGHT_OUTSET = LXB_CSS_VALUE_OUTSET, + LXB_CSS_BORDER_RIGHT__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_border_right_type_t; + + + +enum { + LXB_CSS_BORDER_TOP_THIN = LXB_CSS_VALUE_THIN, + LXB_CSS_BORDER_TOP_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_BORDER_TOP_THICK = LXB_CSS_VALUE_THICK, + LXB_CSS_BORDER_TOP_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_BORDER_TOP_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_BORDER_TOP_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_BORDER_TOP_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_BORDER_TOP_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_BORDER_TOP_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_BORDER_TOP_GROOVE = LXB_CSS_VALUE_GROOVE, + LXB_CSS_BORDER_TOP_RIDGE = LXB_CSS_VALUE_RIDGE, + LXB_CSS_BORDER_TOP_INSET = LXB_CSS_VALUE_INSET, + LXB_CSS_BORDER_TOP_OUTSET = LXB_CSS_VALUE_OUTSET, + LXB_CSS_BORDER_TOP__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_border_top_type_t; + + + +enum { + LXB_CSS_BOTTOM_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_BOTTOM__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_BOTTOM__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_bottom_type_t; + +enum { + LXB_CSS_BOX_SIZING_CONTENT_BOX = LXB_CSS_VALUE_CONTENT_BOX, + LXB_CSS_BOX_SIZING_BORDER_BOX = LXB_CSS_VALUE_BORDER_BOX +}; +typedef unsigned int lxb_css_box_sizing_type_t; + +enum { + LXB_CSS_CLEAR_INLINE_START = LXB_CSS_VALUE_INLINE_START, + LXB_CSS_CLEAR_INLINE_END = LXB_CSS_VALUE_INLINE_END, + LXB_CSS_CLEAR_BLOCK_START = LXB_CSS_VALUE_BLOCK_START, + LXB_CSS_CLEAR_BLOCK_END = LXB_CSS_VALUE_BLOCK_END, + LXB_CSS_CLEAR_LEFT = LXB_CSS_VALUE_LEFT, + LXB_CSS_CLEAR_RIGHT = LXB_CSS_VALUE_RIGHT, + LXB_CSS_CLEAR_TOP = LXB_CSS_VALUE_TOP, + LXB_CSS_CLEAR_BOTTOM = LXB_CSS_VALUE_BOTTOM, + LXB_CSS_CLEAR_NONE = LXB_CSS_VALUE_NONE +}; +typedef unsigned int lxb_css_clear_type_t; + +enum { + LXB_CSS_COLOR_CURRENTCOLOR = LXB_CSS_VALUE_CURRENTCOLOR, + LXB_CSS_COLOR_TRANSPARENT = LXB_CSS_VALUE_TRANSPARENT, + LXB_CSS_COLOR_HEX = LXB_CSS_VALUE_HEX, + LXB_CSS_COLOR_ALICEBLUE = LXB_CSS_VALUE_ALICEBLUE, + LXB_CSS_COLOR_ANTIQUEWHITE = LXB_CSS_VALUE_ANTIQUEWHITE, + LXB_CSS_COLOR_AQUA = LXB_CSS_VALUE_AQUA, + LXB_CSS_COLOR_AQUAMARINE = LXB_CSS_VALUE_AQUAMARINE, + LXB_CSS_COLOR_AZURE = LXB_CSS_VALUE_AZURE, + LXB_CSS_COLOR_BEIGE = LXB_CSS_VALUE_BEIGE, + LXB_CSS_COLOR_BISQUE = LXB_CSS_VALUE_BISQUE, + LXB_CSS_COLOR_BLACK = LXB_CSS_VALUE_BLACK, + LXB_CSS_COLOR_BLANCHEDALMOND = LXB_CSS_VALUE_BLANCHEDALMOND, + LXB_CSS_COLOR_BLUE = LXB_CSS_VALUE_BLUE, + LXB_CSS_COLOR_BLUEVIOLET = LXB_CSS_VALUE_BLUEVIOLET, + LXB_CSS_COLOR_BROWN = LXB_CSS_VALUE_BROWN, + LXB_CSS_COLOR_BURLYWOOD = LXB_CSS_VALUE_BURLYWOOD, + LXB_CSS_COLOR_CADETBLUE = LXB_CSS_VALUE_CADETBLUE, + LXB_CSS_COLOR_CHARTREUSE = LXB_CSS_VALUE_CHARTREUSE, + LXB_CSS_COLOR_CHOCOLATE = LXB_CSS_VALUE_CHOCOLATE, + LXB_CSS_COLOR_CORAL = LXB_CSS_VALUE_CORAL, + LXB_CSS_COLOR_CORNFLOWERBLUE = LXB_CSS_VALUE_CORNFLOWERBLUE, + LXB_CSS_COLOR_CORNSILK = LXB_CSS_VALUE_CORNSILK, + LXB_CSS_COLOR_CRIMSON = LXB_CSS_VALUE_CRIMSON, + LXB_CSS_COLOR_CYAN = LXB_CSS_VALUE_CYAN, + LXB_CSS_COLOR_DARKBLUE = LXB_CSS_VALUE_DARKBLUE, + LXB_CSS_COLOR_DARKCYAN = LXB_CSS_VALUE_DARKCYAN, + LXB_CSS_COLOR_DARKGOLDENROD = LXB_CSS_VALUE_DARKGOLDENROD, + LXB_CSS_COLOR_DARKGRAY = LXB_CSS_VALUE_DARKGRAY, + LXB_CSS_COLOR_DARKGREEN = LXB_CSS_VALUE_DARKGREEN, + LXB_CSS_COLOR_DARKGREY = LXB_CSS_VALUE_DARKGREY, + LXB_CSS_COLOR_DARKKHAKI = LXB_CSS_VALUE_DARKKHAKI, + LXB_CSS_COLOR_DARKMAGENTA = LXB_CSS_VALUE_DARKMAGENTA, + LXB_CSS_COLOR_DARKOLIVEGREEN = LXB_CSS_VALUE_DARKOLIVEGREEN, + LXB_CSS_COLOR_DARKORANGE = LXB_CSS_VALUE_DARKORANGE, + LXB_CSS_COLOR_DARKORCHID = LXB_CSS_VALUE_DARKORCHID, + LXB_CSS_COLOR_DARKRED = LXB_CSS_VALUE_DARKRED, + LXB_CSS_COLOR_DARKSALMON = LXB_CSS_VALUE_DARKSALMON, + LXB_CSS_COLOR_DARKSEAGREEN = LXB_CSS_VALUE_DARKSEAGREEN, + LXB_CSS_COLOR_DARKSLATEBLUE = LXB_CSS_VALUE_DARKSLATEBLUE, + LXB_CSS_COLOR_DARKSLATEGRAY = LXB_CSS_VALUE_DARKSLATEGRAY, + LXB_CSS_COLOR_DARKSLATEGREY = LXB_CSS_VALUE_DARKSLATEGREY, + LXB_CSS_COLOR_DARKTURQUOISE = LXB_CSS_VALUE_DARKTURQUOISE, + LXB_CSS_COLOR_DARKVIOLET = LXB_CSS_VALUE_DARKVIOLET, + LXB_CSS_COLOR_DEEPPINK = LXB_CSS_VALUE_DEEPPINK, + LXB_CSS_COLOR_DEEPSKYBLUE = LXB_CSS_VALUE_DEEPSKYBLUE, + LXB_CSS_COLOR_DIMGRAY = LXB_CSS_VALUE_DIMGRAY, + LXB_CSS_COLOR_DIMGREY = LXB_CSS_VALUE_DIMGREY, + LXB_CSS_COLOR_DODGERBLUE = LXB_CSS_VALUE_DODGERBLUE, + LXB_CSS_COLOR_FIREBRICK = LXB_CSS_VALUE_FIREBRICK, + LXB_CSS_COLOR_FLORALWHITE = LXB_CSS_VALUE_FLORALWHITE, + LXB_CSS_COLOR_FORESTGREEN = LXB_CSS_VALUE_FORESTGREEN, + LXB_CSS_COLOR_FUCHSIA = LXB_CSS_VALUE_FUCHSIA, + LXB_CSS_COLOR_GAINSBORO = LXB_CSS_VALUE_GAINSBORO, + LXB_CSS_COLOR_GHOSTWHITE = LXB_CSS_VALUE_GHOSTWHITE, + LXB_CSS_COLOR_GOLD = LXB_CSS_VALUE_GOLD, + LXB_CSS_COLOR_GOLDENROD = LXB_CSS_VALUE_GOLDENROD, + LXB_CSS_COLOR_GRAY = LXB_CSS_VALUE_GRAY, + LXB_CSS_COLOR_GREEN = LXB_CSS_VALUE_GREEN, + LXB_CSS_COLOR_GREENYELLOW = LXB_CSS_VALUE_GREENYELLOW, + LXB_CSS_COLOR_GREY = LXB_CSS_VALUE_GREY, + LXB_CSS_COLOR_HONEYDEW = LXB_CSS_VALUE_HONEYDEW, + LXB_CSS_COLOR_HOTPINK = LXB_CSS_VALUE_HOTPINK, + LXB_CSS_COLOR_INDIANRED = LXB_CSS_VALUE_INDIANRED, + LXB_CSS_COLOR_INDIGO = LXB_CSS_VALUE_INDIGO, + LXB_CSS_COLOR_IVORY = LXB_CSS_VALUE_IVORY, + LXB_CSS_COLOR_KHAKI = LXB_CSS_VALUE_KHAKI, + LXB_CSS_COLOR_LAVENDER = LXB_CSS_VALUE_LAVENDER, + LXB_CSS_COLOR_LAVENDERBLUSH = LXB_CSS_VALUE_LAVENDERBLUSH, + LXB_CSS_COLOR_LAWNGREEN = LXB_CSS_VALUE_LAWNGREEN, + LXB_CSS_COLOR_LEMONCHIFFON = LXB_CSS_VALUE_LEMONCHIFFON, + LXB_CSS_COLOR_LIGHTBLUE = LXB_CSS_VALUE_LIGHTBLUE, + LXB_CSS_COLOR_LIGHTCORAL = LXB_CSS_VALUE_LIGHTCORAL, + LXB_CSS_COLOR_LIGHTCYAN = LXB_CSS_VALUE_LIGHTCYAN, + LXB_CSS_COLOR_LIGHTGOLDENRODYELLOW = LXB_CSS_VALUE_LIGHTGOLDENRODYELLOW, + LXB_CSS_COLOR_LIGHTGRAY = LXB_CSS_VALUE_LIGHTGRAY, + LXB_CSS_COLOR_LIGHTGREEN = LXB_CSS_VALUE_LIGHTGREEN, + LXB_CSS_COLOR_LIGHTGREY = LXB_CSS_VALUE_LIGHTGREY, + LXB_CSS_COLOR_LIGHTPINK = LXB_CSS_VALUE_LIGHTPINK, + LXB_CSS_COLOR_LIGHTSALMON = LXB_CSS_VALUE_LIGHTSALMON, + LXB_CSS_COLOR_LIGHTSEAGREEN = LXB_CSS_VALUE_LIGHTSEAGREEN, + LXB_CSS_COLOR_LIGHTSKYBLUE = LXB_CSS_VALUE_LIGHTSKYBLUE, + LXB_CSS_COLOR_LIGHTSLATEGRAY = LXB_CSS_VALUE_LIGHTSLATEGRAY, + LXB_CSS_COLOR_LIGHTSLATEGREY = LXB_CSS_VALUE_LIGHTSLATEGREY, + LXB_CSS_COLOR_LIGHTSTEELBLUE = LXB_CSS_VALUE_LIGHTSTEELBLUE, + LXB_CSS_COLOR_LIGHTYELLOW = LXB_CSS_VALUE_LIGHTYELLOW, + LXB_CSS_COLOR_LIME = LXB_CSS_VALUE_LIME, + LXB_CSS_COLOR_LIMEGREEN = LXB_CSS_VALUE_LIMEGREEN, + LXB_CSS_COLOR_LINEN = LXB_CSS_VALUE_LINEN, + LXB_CSS_COLOR_MAGENTA = LXB_CSS_VALUE_MAGENTA, + LXB_CSS_COLOR_MAROON = LXB_CSS_VALUE_MAROON, + LXB_CSS_COLOR_MEDIUMAQUAMARINE = LXB_CSS_VALUE_MEDIUMAQUAMARINE, + LXB_CSS_COLOR_MEDIUMBLUE = LXB_CSS_VALUE_MEDIUMBLUE, + LXB_CSS_COLOR_MEDIUMORCHID = LXB_CSS_VALUE_MEDIUMORCHID, + LXB_CSS_COLOR_MEDIUMPURPLE = LXB_CSS_VALUE_MEDIUMPURPLE, + LXB_CSS_COLOR_MEDIUMSEAGREEN = LXB_CSS_VALUE_MEDIUMSEAGREEN, + LXB_CSS_COLOR_MEDIUMSLATEBLUE = LXB_CSS_VALUE_MEDIUMSLATEBLUE, + LXB_CSS_COLOR_MEDIUMSPRINGGREEN = LXB_CSS_VALUE_MEDIUMSPRINGGREEN, + LXB_CSS_COLOR_MEDIUMTURQUOISE = LXB_CSS_VALUE_MEDIUMTURQUOISE, + LXB_CSS_COLOR_MEDIUMVIOLETRED = LXB_CSS_VALUE_MEDIUMVIOLETRED, + LXB_CSS_COLOR_MIDNIGHTBLUE = LXB_CSS_VALUE_MIDNIGHTBLUE, + LXB_CSS_COLOR_MINTCREAM = LXB_CSS_VALUE_MINTCREAM, + LXB_CSS_COLOR_MISTYROSE = LXB_CSS_VALUE_MISTYROSE, + LXB_CSS_COLOR_MOCCASIN = LXB_CSS_VALUE_MOCCASIN, + LXB_CSS_COLOR_NAVAJOWHITE = LXB_CSS_VALUE_NAVAJOWHITE, + LXB_CSS_COLOR_NAVY = LXB_CSS_VALUE_NAVY, + LXB_CSS_COLOR_OLDLACE = LXB_CSS_VALUE_OLDLACE, + LXB_CSS_COLOR_OLIVE = LXB_CSS_VALUE_OLIVE, + LXB_CSS_COLOR_OLIVEDRAB = LXB_CSS_VALUE_OLIVEDRAB, + LXB_CSS_COLOR_ORANGE = LXB_CSS_VALUE_ORANGE, + LXB_CSS_COLOR_ORANGERED = LXB_CSS_VALUE_ORANGERED, + LXB_CSS_COLOR_ORCHID = LXB_CSS_VALUE_ORCHID, + LXB_CSS_COLOR_PALEGOLDENROD = LXB_CSS_VALUE_PALEGOLDENROD, + LXB_CSS_COLOR_PALEGREEN = LXB_CSS_VALUE_PALEGREEN, + LXB_CSS_COLOR_PALETURQUOISE = LXB_CSS_VALUE_PALETURQUOISE, + LXB_CSS_COLOR_PALEVIOLETRED = LXB_CSS_VALUE_PALEVIOLETRED, + LXB_CSS_COLOR_PAPAYAWHIP = LXB_CSS_VALUE_PAPAYAWHIP, + LXB_CSS_COLOR_PEACHPUFF = LXB_CSS_VALUE_PEACHPUFF, + LXB_CSS_COLOR_PERU = LXB_CSS_VALUE_PERU, + LXB_CSS_COLOR_PINK = LXB_CSS_VALUE_PINK, + LXB_CSS_COLOR_PLUM = LXB_CSS_VALUE_PLUM, + LXB_CSS_COLOR_POWDERBLUE = LXB_CSS_VALUE_POWDERBLUE, + LXB_CSS_COLOR_PURPLE = LXB_CSS_VALUE_PURPLE, + LXB_CSS_COLOR_REBECCAPURPLE = LXB_CSS_VALUE_REBECCAPURPLE, + LXB_CSS_COLOR_RED = LXB_CSS_VALUE_RED, + LXB_CSS_COLOR_ROSYBROWN = LXB_CSS_VALUE_ROSYBROWN, + LXB_CSS_COLOR_ROYALBLUE = LXB_CSS_VALUE_ROYALBLUE, + LXB_CSS_COLOR_SADDLEBROWN = LXB_CSS_VALUE_SADDLEBROWN, + LXB_CSS_COLOR_SALMON = LXB_CSS_VALUE_SALMON, + LXB_CSS_COLOR_SANDYBROWN = LXB_CSS_VALUE_SANDYBROWN, + LXB_CSS_COLOR_SEAGREEN = LXB_CSS_VALUE_SEAGREEN, + LXB_CSS_COLOR_SEASHELL = LXB_CSS_VALUE_SEASHELL, + LXB_CSS_COLOR_SIENNA = LXB_CSS_VALUE_SIENNA, + LXB_CSS_COLOR_SILVER = LXB_CSS_VALUE_SILVER, + LXB_CSS_COLOR_SKYBLUE = LXB_CSS_VALUE_SKYBLUE, + LXB_CSS_COLOR_SLATEBLUE = LXB_CSS_VALUE_SLATEBLUE, + LXB_CSS_COLOR_SLATEGRAY = LXB_CSS_VALUE_SLATEGRAY, + LXB_CSS_COLOR_SLATEGREY = LXB_CSS_VALUE_SLATEGREY, + LXB_CSS_COLOR_SNOW = LXB_CSS_VALUE_SNOW, + LXB_CSS_COLOR_SPRINGGREEN = LXB_CSS_VALUE_SPRINGGREEN, + LXB_CSS_COLOR_STEELBLUE = LXB_CSS_VALUE_STEELBLUE, + LXB_CSS_COLOR_TAN = LXB_CSS_VALUE_TAN, + LXB_CSS_COLOR_TEAL = LXB_CSS_VALUE_TEAL, + LXB_CSS_COLOR_THISTLE = LXB_CSS_VALUE_THISTLE, + LXB_CSS_COLOR_TOMATO = LXB_CSS_VALUE_TOMATO, + LXB_CSS_COLOR_TURQUOISE = LXB_CSS_VALUE_TURQUOISE, + LXB_CSS_COLOR_VIOLET = LXB_CSS_VALUE_VIOLET, + LXB_CSS_COLOR_WHEAT = LXB_CSS_VALUE_WHEAT, + LXB_CSS_COLOR_WHITE = LXB_CSS_VALUE_WHITE, + LXB_CSS_COLOR_WHITESMOKE = LXB_CSS_VALUE_WHITESMOKE, + LXB_CSS_COLOR_YELLOW = LXB_CSS_VALUE_YELLOW, + LXB_CSS_COLOR_YELLOWGREEN = LXB_CSS_VALUE_YELLOWGREEN, + LXB_CSS_COLOR_CANVAS = LXB_CSS_VALUE_CANVAS, + LXB_CSS_COLOR_CANVASTEXT = LXB_CSS_VALUE_CANVASTEXT, + LXB_CSS_COLOR_LINKTEXT = LXB_CSS_VALUE_LINKTEXT, + LXB_CSS_COLOR_VISITEDTEXT = LXB_CSS_VALUE_VISITEDTEXT, + LXB_CSS_COLOR_ACTIVETEXT = LXB_CSS_VALUE_ACTIVETEXT, + LXB_CSS_COLOR_BUTTONFACE = LXB_CSS_VALUE_BUTTONFACE, + LXB_CSS_COLOR_BUTTONTEXT = LXB_CSS_VALUE_BUTTONTEXT, + LXB_CSS_COLOR_BUTTONBORDER = LXB_CSS_VALUE_BUTTONBORDER, + LXB_CSS_COLOR_FIELD = LXB_CSS_VALUE_FIELD, + LXB_CSS_COLOR_FIELDTEXT = LXB_CSS_VALUE_FIELDTEXT, + LXB_CSS_COLOR_HIGHLIGHT = LXB_CSS_VALUE_HIGHLIGHT, + LXB_CSS_COLOR_HIGHLIGHTTEXT = LXB_CSS_VALUE_HIGHLIGHTTEXT, + LXB_CSS_COLOR_SELECTEDITEM = LXB_CSS_VALUE_SELECTEDITEM, + LXB_CSS_COLOR_SELECTEDITEMTEXT = LXB_CSS_VALUE_SELECTEDITEMTEXT, + LXB_CSS_COLOR_MARK = LXB_CSS_VALUE_MARK, + LXB_CSS_COLOR_MARKTEXT = LXB_CSS_VALUE_MARKTEXT, + LXB_CSS_COLOR_GRAYTEXT = LXB_CSS_VALUE_GRAYTEXT, + LXB_CSS_COLOR_ACCENTCOLOR = LXB_CSS_VALUE_ACCENTCOLOR, + LXB_CSS_COLOR_ACCENTCOLORTEXT = LXB_CSS_VALUE_ACCENTCOLORTEXT, + LXB_CSS_COLOR_RGB = LXB_CSS_VALUE_RGB, + LXB_CSS_COLOR_RGBA = LXB_CSS_VALUE_RGBA, + LXB_CSS_COLOR_HSL = LXB_CSS_VALUE_HSL, + LXB_CSS_COLOR_HSLA = LXB_CSS_VALUE_HSLA, + LXB_CSS_COLOR_HWB = LXB_CSS_VALUE_HWB, + LXB_CSS_COLOR_LAB = LXB_CSS_VALUE_LAB, + LXB_CSS_COLOR_LCH = LXB_CSS_VALUE_LCH, + LXB_CSS_COLOR_OKLAB = LXB_CSS_VALUE_OKLAB, + LXB_CSS_COLOR_OKLCH = LXB_CSS_VALUE_OKLCH, + LXB_CSS_COLOR_COLOR = LXB_CSS_VALUE_COLOR +}; +typedef unsigned int lxb_css_color_type_t; + +enum { + LXB_CSS_DIRECTION_LTR = LXB_CSS_VALUE_LTR, + LXB_CSS_DIRECTION_RTL = LXB_CSS_VALUE_RTL +}; +typedef unsigned int lxb_css_direction_type_t; + +enum { + LXB_CSS_DISPLAY_BLOCK = LXB_CSS_VALUE_BLOCK, + LXB_CSS_DISPLAY_INLINE = LXB_CSS_VALUE_INLINE, + LXB_CSS_DISPLAY_RUN_IN = LXB_CSS_VALUE_RUN_IN, + LXB_CSS_DISPLAY_FLOW = LXB_CSS_VALUE_FLOW, + LXB_CSS_DISPLAY_FLOW_ROOT = LXB_CSS_VALUE_FLOW_ROOT, + LXB_CSS_DISPLAY_TABLE = LXB_CSS_VALUE_TABLE, + LXB_CSS_DISPLAY_FLEX = LXB_CSS_VALUE_FLEX, + LXB_CSS_DISPLAY_GRID = LXB_CSS_VALUE_GRID, + LXB_CSS_DISPLAY_RUBY = LXB_CSS_VALUE_RUBY, + LXB_CSS_DISPLAY_LIST_ITEM = LXB_CSS_VALUE_LIST_ITEM, + LXB_CSS_DISPLAY_TABLE_ROW_GROUP = LXB_CSS_VALUE_TABLE_ROW_GROUP, + LXB_CSS_DISPLAY_TABLE_HEADER_GROUP = LXB_CSS_VALUE_TABLE_HEADER_GROUP, + LXB_CSS_DISPLAY_TABLE_FOOTER_GROUP = LXB_CSS_VALUE_TABLE_FOOTER_GROUP, + LXB_CSS_DISPLAY_TABLE_ROW = LXB_CSS_VALUE_TABLE_ROW, + LXB_CSS_DISPLAY_TABLE_CELL = LXB_CSS_VALUE_TABLE_CELL, + LXB_CSS_DISPLAY_TABLE_COLUMN_GROUP = LXB_CSS_VALUE_TABLE_COLUMN_GROUP, + LXB_CSS_DISPLAY_TABLE_COLUMN = LXB_CSS_VALUE_TABLE_COLUMN, + LXB_CSS_DISPLAY_TABLE_CAPTION = LXB_CSS_VALUE_TABLE_CAPTION, + LXB_CSS_DISPLAY_RUBY_BASE = LXB_CSS_VALUE_RUBY_BASE, + LXB_CSS_DISPLAY_RUBY_TEXT = LXB_CSS_VALUE_RUBY_TEXT, + LXB_CSS_DISPLAY_RUBY_BASE_CONTAINER = LXB_CSS_VALUE_RUBY_BASE_CONTAINER, + LXB_CSS_DISPLAY_RUBY_TEXT_CONTAINER = LXB_CSS_VALUE_RUBY_TEXT_CONTAINER, + LXB_CSS_DISPLAY_CONTENTS = LXB_CSS_VALUE_CONTENTS, + LXB_CSS_DISPLAY_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_DISPLAY_INLINE_BLOCK = LXB_CSS_VALUE_INLINE_BLOCK, + LXB_CSS_DISPLAY_INLINE_TABLE = LXB_CSS_VALUE_INLINE_TABLE, + LXB_CSS_DISPLAY_INLINE_FLEX = LXB_CSS_VALUE_INLINE_FLEX, + LXB_CSS_DISPLAY_INLINE_GRID = LXB_CSS_VALUE_INLINE_GRID +}; +typedef unsigned int lxb_css_display_type_t; + +enum { + LXB_CSS_DOMINANT_BASELINE_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_DOMINANT_BASELINE_TEXT_BOTTOM = LXB_CSS_VALUE_TEXT_BOTTOM, + LXB_CSS_DOMINANT_BASELINE_ALPHABETIC = LXB_CSS_VALUE_ALPHABETIC, + LXB_CSS_DOMINANT_BASELINE_IDEOGRAPHIC = LXB_CSS_VALUE_IDEOGRAPHIC, + LXB_CSS_DOMINANT_BASELINE_MIDDLE = LXB_CSS_VALUE_MIDDLE, + LXB_CSS_DOMINANT_BASELINE_CENTRAL = LXB_CSS_VALUE_CENTRAL, + LXB_CSS_DOMINANT_BASELINE_MATHEMATICAL = LXB_CSS_VALUE_MATHEMATICAL, + LXB_CSS_DOMINANT_BASELINE_HANGING = LXB_CSS_VALUE_HANGING, + LXB_CSS_DOMINANT_BASELINE_TEXT_TOP = LXB_CSS_VALUE_TEXT_TOP +}; +typedef unsigned int lxb_css_dominant_baseline_type_t; + +enum { + LXB_CSS_FLEX_NONE = LXB_CSS_VALUE_NONE +}; +typedef unsigned int lxb_css_flex_type_t; + +enum { + LXB_CSS_FLEX_BASIS_CONTENT = LXB_CSS_VALUE_CONTENT +}; +typedef unsigned int lxb_css_flex_basis_type_t; + +enum { + LXB_CSS_FLEX_DIRECTION_ROW = LXB_CSS_VALUE_ROW, + LXB_CSS_FLEX_DIRECTION_ROW_REVERSE = LXB_CSS_VALUE_ROW_REVERSE, + LXB_CSS_FLEX_DIRECTION_COLUMN = LXB_CSS_VALUE_COLUMN, + LXB_CSS_FLEX_DIRECTION_COLUMN_REVERSE = LXB_CSS_VALUE_COLUMN_REVERSE +}; +typedef unsigned int lxb_css_flex_direction_type_t; + + + +enum { + LXB_CSS_FLEX_GROW__NUMBER = LXB_CSS_VALUE__NUMBER +}; +typedef unsigned int lxb_css_flex_grow_type_t; + +enum { + LXB_CSS_FLEX_SHRINK__NUMBER = LXB_CSS_VALUE__NUMBER +}; +typedef unsigned int lxb_css_flex_shrink_type_t; + +enum { + LXB_CSS_FLEX_WRAP_NOWRAP = LXB_CSS_VALUE_NOWRAP, + LXB_CSS_FLEX_WRAP_WRAP = LXB_CSS_VALUE_WRAP, + LXB_CSS_FLEX_WRAP_WRAP_REVERSE = LXB_CSS_VALUE_WRAP_REVERSE +}; +typedef unsigned int lxb_css_flex_wrap_type_t; + +enum { + LXB_CSS_FLOAT_BLOCK_START = LXB_CSS_VALUE_BLOCK_START, + LXB_CSS_FLOAT_BLOCK_END = LXB_CSS_VALUE_BLOCK_END, + LXB_CSS_FLOAT_INLINE_START = LXB_CSS_VALUE_INLINE_START, + LXB_CSS_FLOAT_INLINE_END = LXB_CSS_VALUE_INLINE_END, + LXB_CSS_FLOAT_SNAP_BLOCK = LXB_CSS_VALUE_SNAP_BLOCK, + LXB_CSS_FLOAT_START = LXB_CSS_VALUE_START, + LXB_CSS_FLOAT_END = LXB_CSS_VALUE_END, + LXB_CSS_FLOAT_NEAR = LXB_CSS_VALUE_NEAR, + LXB_CSS_FLOAT_SNAP_INLINE = LXB_CSS_VALUE_SNAP_INLINE, + LXB_CSS_FLOAT_LEFT = LXB_CSS_VALUE_LEFT, + LXB_CSS_FLOAT_RIGHT = LXB_CSS_VALUE_RIGHT, + LXB_CSS_FLOAT_TOP = LXB_CSS_VALUE_TOP, + LXB_CSS_FLOAT_BOTTOM = LXB_CSS_VALUE_BOTTOM, + LXB_CSS_FLOAT_NONE = LXB_CSS_VALUE_NONE +}; +typedef unsigned int lxb_css_float_type_t; + +enum { + LXB_CSS_FLOAT_DEFER__INTEGER = LXB_CSS_VALUE__INTEGER, + LXB_CSS_FLOAT_DEFER_LAST = LXB_CSS_VALUE_LAST, + LXB_CSS_FLOAT_DEFER_NONE = LXB_CSS_VALUE_NONE +}; +typedef unsigned int lxb_css_float_defer_type_t; + +enum { + LXB_CSS_FLOAT_OFFSET__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_FLOAT_OFFSET__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_float_offset_type_t; + +enum { + LXB_CSS_FLOAT_REFERENCE_INLINE = LXB_CSS_VALUE_INLINE, + LXB_CSS_FLOAT_REFERENCE_COLUMN = LXB_CSS_VALUE_COLUMN, + LXB_CSS_FLOAT_REFERENCE_REGION = LXB_CSS_VALUE_REGION, + LXB_CSS_FLOAT_REFERENCE_PAGE = LXB_CSS_VALUE_PAGE +}; +typedef unsigned int lxb_css_float_reference_type_t; + +enum { + LXB_CSS_FONT_FAMILY_SERIF = LXB_CSS_VALUE_SERIF, + LXB_CSS_FONT_FAMILY_SANS_SERIF = LXB_CSS_VALUE_SANS_SERIF, + LXB_CSS_FONT_FAMILY_CURSIVE = LXB_CSS_VALUE_CURSIVE, + LXB_CSS_FONT_FAMILY_FANTASY = LXB_CSS_VALUE_FANTASY, + LXB_CSS_FONT_FAMILY_MONOSPACE = LXB_CSS_VALUE_MONOSPACE, + LXB_CSS_FONT_FAMILY_SYSTEM_UI = LXB_CSS_VALUE_SYSTEM_UI, + LXB_CSS_FONT_FAMILY_EMOJI = LXB_CSS_VALUE_EMOJI, + LXB_CSS_FONT_FAMILY_MATH = LXB_CSS_VALUE_MATH, + LXB_CSS_FONT_FAMILY_FANGSONG = LXB_CSS_VALUE_FANGSONG, + LXB_CSS_FONT_FAMILY_UI_SERIF = LXB_CSS_VALUE_UI_SERIF, + LXB_CSS_FONT_FAMILY_UI_SANS_SERIF = LXB_CSS_VALUE_UI_SANS_SERIF, + LXB_CSS_FONT_FAMILY_UI_MONOSPACE = LXB_CSS_VALUE_UI_MONOSPACE, + LXB_CSS_FONT_FAMILY_UI_ROUNDED = LXB_CSS_VALUE_UI_ROUNDED +}; +typedef unsigned int lxb_css_font_family_type_t; + +enum { + LXB_CSS_FONT_SIZE_XX_SMALL = LXB_CSS_VALUE_XX_SMALL, + LXB_CSS_FONT_SIZE_X_SMALL = LXB_CSS_VALUE_X_SMALL, + LXB_CSS_FONT_SIZE_SMALL = LXB_CSS_VALUE_SMALL, + LXB_CSS_FONT_SIZE_MEDIUM = LXB_CSS_VALUE_MEDIUM, + LXB_CSS_FONT_SIZE_LARGE = LXB_CSS_VALUE_LARGE, + LXB_CSS_FONT_SIZE_X_LARGE = LXB_CSS_VALUE_X_LARGE, + LXB_CSS_FONT_SIZE_XX_LARGE = LXB_CSS_VALUE_XX_LARGE, + LXB_CSS_FONT_SIZE_XXX_LARGE = LXB_CSS_VALUE_XXX_LARGE, + LXB_CSS_FONT_SIZE_LARGER = LXB_CSS_VALUE_LARGER, + LXB_CSS_FONT_SIZE_SMALLER = LXB_CSS_VALUE_SMALLER, + LXB_CSS_FONT_SIZE_MATH = LXB_CSS_VALUE_MATH, + LXB_CSS_FONT_SIZE__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_font_size_type_t; + +enum { + LXB_CSS_FONT_STRETCH_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_FONT_STRETCH__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_FONT_STRETCH_ULTRA_CONDENSED = LXB_CSS_VALUE_ULTRA_CONDENSED, + LXB_CSS_FONT_STRETCH_EXTRA_CONDENSED = LXB_CSS_VALUE_EXTRA_CONDENSED, + LXB_CSS_FONT_STRETCH_CONDENSED = LXB_CSS_VALUE_CONDENSED, + LXB_CSS_FONT_STRETCH_SEMI_CONDENSED = LXB_CSS_VALUE_SEMI_CONDENSED, + LXB_CSS_FONT_STRETCH_SEMI_EXPANDED = LXB_CSS_VALUE_SEMI_EXPANDED, + LXB_CSS_FONT_STRETCH_EXPANDED = LXB_CSS_VALUE_EXPANDED, + LXB_CSS_FONT_STRETCH_EXTRA_EXPANDED = LXB_CSS_VALUE_EXTRA_EXPANDED, + LXB_CSS_FONT_STRETCH_ULTRA_EXPANDED = LXB_CSS_VALUE_ULTRA_EXPANDED +}; +typedef unsigned int lxb_css_font_stretch_type_t; + +enum { + LXB_CSS_FONT_STYLE_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_FONT_STYLE_ITALIC = LXB_CSS_VALUE_ITALIC, + LXB_CSS_FONT_STYLE_OBLIQUE = LXB_CSS_VALUE_OBLIQUE +}; +typedef unsigned int lxb_css_font_style_type_t; + +enum { + LXB_CSS_FONT_WEIGHT_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_FONT_WEIGHT_BOLD = LXB_CSS_VALUE_BOLD, + LXB_CSS_FONT_WEIGHT__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_FONT_WEIGHT_BOLDER = LXB_CSS_VALUE_BOLDER, + LXB_CSS_FONT_WEIGHT_LIGHTER = LXB_CSS_VALUE_LIGHTER +}; +typedef unsigned int lxb_css_font_weight_type_t; + +enum { + LXB_CSS_HANGING_PUNCTUATION_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_HANGING_PUNCTUATION_FIRST = LXB_CSS_VALUE_FIRST, + LXB_CSS_HANGING_PUNCTUATION_FORCE_END = LXB_CSS_VALUE_FORCE_END, + LXB_CSS_HANGING_PUNCTUATION_ALLOW_END = LXB_CSS_VALUE_ALLOW_END, + LXB_CSS_HANGING_PUNCTUATION_LAST = LXB_CSS_VALUE_LAST +}; +typedef unsigned int lxb_css_hanging_punctuation_type_t; + +enum { + LXB_CSS_HEIGHT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_HEIGHT_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_HEIGHT_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_HEIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_HEIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_HEIGHT__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_HEIGHT__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_height_type_t; + +enum { + LXB_CSS_HYPHENS_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_HYPHENS_MANUAL = LXB_CSS_VALUE_MANUAL, + LXB_CSS_HYPHENS_AUTO = LXB_CSS_VALUE_AUTO +}; +typedef unsigned int lxb_css_hyphens_type_t; + +enum { + LXB_CSS_INSET_BLOCK_END_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_INSET_BLOCK_END__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_INSET_BLOCK_END__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_inset_block_end_type_t; + +enum { + LXB_CSS_INSET_BLOCK_START_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_INSET_BLOCK_START__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_INSET_BLOCK_START__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_inset_block_start_type_t; + +enum { + LXB_CSS_INSET_INLINE_END_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_INSET_INLINE_END__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_INSET_INLINE_END__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_inset_inline_end_type_t; + +enum { + LXB_CSS_INSET_INLINE_START_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_INSET_INLINE_START__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_INSET_INLINE_START__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_inset_inline_start_type_t; + +enum { + LXB_CSS_JUSTIFY_CONTENT_FLEX_START = LXB_CSS_VALUE_FLEX_START, + LXB_CSS_JUSTIFY_CONTENT_FLEX_END = LXB_CSS_VALUE_FLEX_END, + LXB_CSS_JUSTIFY_CONTENT_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_JUSTIFY_CONTENT_SPACE_BETWEEN = LXB_CSS_VALUE_SPACE_BETWEEN, + LXB_CSS_JUSTIFY_CONTENT_SPACE_AROUND = LXB_CSS_VALUE_SPACE_AROUND +}; +typedef unsigned int lxb_css_justify_content_type_t; + +enum { + LXB_CSS_LEFT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_LEFT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_LEFT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_left_type_t; + +enum { + LXB_CSS_LETTER_SPACING_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_LETTER_SPACING__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_letter_spacing_type_t; + +enum { + LXB_CSS_LINE_BREAK_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_LINE_BREAK_LOOSE = LXB_CSS_VALUE_LOOSE, + LXB_CSS_LINE_BREAK_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_LINE_BREAK_STRICT = LXB_CSS_VALUE_STRICT, + LXB_CSS_LINE_BREAK_ANYWHERE = LXB_CSS_VALUE_ANYWHERE +}; +typedef unsigned int lxb_css_line_break_type_t; + +enum { + LXB_CSS_LINE_HEIGHT_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_LINE_HEIGHT__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_LINE_HEIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_LINE_HEIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_line_height_type_t; + +enum { + LXB_CSS_MARGIN_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MARGIN__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MARGIN__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_margin_type_t; + +enum { + LXB_CSS_MARGIN_BOTTOM_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MARGIN_BOTTOM__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MARGIN_BOTTOM__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_margin_bottom_type_t; + +enum { + LXB_CSS_MARGIN_LEFT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MARGIN_LEFT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MARGIN_LEFT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_margin_left_type_t; + +enum { + LXB_CSS_MARGIN_RIGHT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MARGIN_RIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MARGIN_RIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_margin_right_type_t; + +enum { + LXB_CSS_MARGIN_TOP_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MARGIN_TOP__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MARGIN_TOP__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_margin_top_type_t; + +enum { + LXB_CSS_MAX_HEIGHT_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_MAX_HEIGHT_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_MAX_HEIGHT_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_MAX_HEIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MAX_HEIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_MAX_HEIGHT__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_MAX_HEIGHT__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_max_height_type_t; + +enum { + LXB_CSS_MAX_WIDTH_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_MAX_WIDTH_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_MAX_WIDTH_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_MAX_WIDTH__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MAX_WIDTH__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_MAX_WIDTH__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_MAX_WIDTH__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_max_width_type_t; + +enum { + LXB_CSS_MIN_HEIGHT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MIN_HEIGHT_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_MIN_HEIGHT_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_MIN_HEIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MIN_HEIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_MIN_HEIGHT__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_MIN_HEIGHT__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_min_height_type_t; + +enum { + LXB_CSS_MIN_WIDTH_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_MIN_WIDTH_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_MIN_WIDTH_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_MIN_WIDTH__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_MIN_WIDTH__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_MIN_WIDTH__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_MIN_WIDTH__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_min_width_type_t; + +enum { + LXB_CSS_OPACITY__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_OPACITY__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_opacity_type_t; + +enum { + LXB_CSS_ORDER__INTEGER = LXB_CSS_VALUE__INTEGER +}; +typedef unsigned int lxb_css_order_type_t; + +enum { + LXB_CSS_OVERFLOW_BLOCK_VISIBLE = LXB_CSS_VALUE_VISIBLE, + LXB_CSS_OVERFLOW_BLOCK_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_OVERFLOW_BLOCK_CLIP = LXB_CSS_VALUE_CLIP, + LXB_CSS_OVERFLOW_BLOCK_SCROLL = LXB_CSS_VALUE_SCROLL, + LXB_CSS_OVERFLOW_BLOCK_AUTO = LXB_CSS_VALUE_AUTO +}; +typedef unsigned int lxb_css_overflow_block_type_t; + +enum { + LXB_CSS_OVERFLOW_INLINE_VISIBLE = LXB_CSS_VALUE_VISIBLE, + LXB_CSS_OVERFLOW_INLINE_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_OVERFLOW_INLINE_CLIP = LXB_CSS_VALUE_CLIP, + LXB_CSS_OVERFLOW_INLINE_SCROLL = LXB_CSS_VALUE_SCROLL, + LXB_CSS_OVERFLOW_INLINE_AUTO = LXB_CSS_VALUE_AUTO +}; +typedef unsigned int lxb_css_overflow_inline_type_t; + +enum { + LXB_CSS_OVERFLOW_WRAP_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_OVERFLOW_WRAP_BREAK_WORD = LXB_CSS_VALUE_BREAK_WORD, + LXB_CSS_OVERFLOW_WRAP_ANYWHERE = LXB_CSS_VALUE_ANYWHERE +}; +typedef unsigned int lxb_css_overflow_wrap_type_t; + +enum { + LXB_CSS_OVERFLOW_X_VISIBLE = LXB_CSS_VALUE_VISIBLE, + LXB_CSS_OVERFLOW_X_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_OVERFLOW_X_CLIP = LXB_CSS_VALUE_CLIP, + LXB_CSS_OVERFLOW_X_SCROLL = LXB_CSS_VALUE_SCROLL, + LXB_CSS_OVERFLOW_X_AUTO = LXB_CSS_VALUE_AUTO +}; +typedef unsigned int lxb_css_overflow_x_type_t; + +enum { + LXB_CSS_OVERFLOW_Y_VISIBLE = LXB_CSS_VALUE_VISIBLE, + LXB_CSS_OVERFLOW_Y_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_OVERFLOW_Y_CLIP = LXB_CSS_VALUE_CLIP, + LXB_CSS_OVERFLOW_Y_SCROLL = LXB_CSS_VALUE_SCROLL, + LXB_CSS_OVERFLOW_Y_AUTO = LXB_CSS_VALUE_AUTO +}; +typedef unsigned int lxb_css_overflow_y_type_t; + +enum { + LXB_CSS_PADDING_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_PADDING__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_PADDING__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_padding_type_t; + +enum { + LXB_CSS_PADDING_BOTTOM_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_PADDING_BOTTOM__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_PADDING_BOTTOM__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_padding_bottom_type_t; + +enum { + LXB_CSS_PADDING_LEFT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_PADDING_LEFT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_PADDING_LEFT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_padding_left_type_t; + +enum { + LXB_CSS_PADDING_RIGHT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_PADDING_RIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_PADDING_RIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_padding_right_type_t; + +enum { + LXB_CSS_PADDING_TOP_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_PADDING_TOP__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_PADDING_TOP__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_padding_top_type_t; + +enum { + LXB_CSS_POSITION_STATIC = LXB_CSS_VALUE_STATIC, + LXB_CSS_POSITION_RELATIVE = LXB_CSS_VALUE_RELATIVE, + LXB_CSS_POSITION_ABSOLUTE = LXB_CSS_VALUE_ABSOLUTE, + LXB_CSS_POSITION_STICKY = LXB_CSS_VALUE_STICKY, + LXB_CSS_POSITION_FIXED = LXB_CSS_VALUE_FIXED +}; +typedef unsigned int lxb_css_position_type_t; + +enum { + LXB_CSS_RIGHT_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_RIGHT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_RIGHT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_right_type_t; + +enum { + LXB_CSS_TAB_SIZE__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_TAB_SIZE__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_tab_size_type_t; + +enum { + LXB_CSS_TEXT_ALIGN_START = LXB_CSS_VALUE_START, + LXB_CSS_TEXT_ALIGN_END = LXB_CSS_VALUE_END, + LXB_CSS_TEXT_ALIGN_LEFT = LXB_CSS_VALUE_LEFT, + LXB_CSS_TEXT_ALIGN_RIGHT = LXB_CSS_VALUE_RIGHT, + LXB_CSS_TEXT_ALIGN_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_TEXT_ALIGN_JUSTIFY = LXB_CSS_VALUE_JUSTIFY, + LXB_CSS_TEXT_ALIGN_MATCH_PARENT = LXB_CSS_VALUE_MATCH_PARENT, + LXB_CSS_TEXT_ALIGN_JUSTIFY_ALL = LXB_CSS_VALUE_JUSTIFY_ALL +}; +typedef unsigned int lxb_css_text_align_type_t; + +enum { + LXB_CSS_TEXT_ALIGN_ALL_START = LXB_CSS_VALUE_START, + LXB_CSS_TEXT_ALIGN_ALL_END = LXB_CSS_VALUE_END, + LXB_CSS_TEXT_ALIGN_ALL_LEFT = LXB_CSS_VALUE_LEFT, + LXB_CSS_TEXT_ALIGN_ALL_RIGHT = LXB_CSS_VALUE_RIGHT, + LXB_CSS_TEXT_ALIGN_ALL_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_TEXT_ALIGN_ALL_JUSTIFY = LXB_CSS_VALUE_JUSTIFY, + LXB_CSS_TEXT_ALIGN_ALL_MATCH_PARENT = LXB_CSS_VALUE_MATCH_PARENT +}; +typedef unsigned int lxb_css_text_align_all_type_t; + +enum { + LXB_CSS_TEXT_ALIGN_LAST_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_TEXT_ALIGN_LAST_START = LXB_CSS_VALUE_START, + LXB_CSS_TEXT_ALIGN_LAST_END = LXB_CSS_VALUE_END, + LXB_CSS_TEXT_ALIGN_LAST_LEFT = LXB_CSS_VALUE_LEFT, + LXB_CSS_TEXT_ALIGN_LAST_RIGHT = LXB_CSS_VALUE_RIGHT, + LXB_CSS_TEXT_ALIGN_LAST_CENTER = LXB_CSS_VALUE_CENTER, + LXB_CSS_TEXT_ALIGN_LAST_JUSTIFY = LXB_CSS_VALUE_JUSTIFY, + LXB_CSS_TEXT_ALIGN_LAST_MATCH_PARENT = LXB_CSS_VALUE_MATCH_PARENT +}; +typedef unsigned int lxb_css_text_align_last_type_t; + +enum { + LXB_CSS_TEXT_COMBINE_UPRIGHT_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_TEXT_COMBINE_UPRIGHT_ALL = LXB_CSS_VALUE_ALL, + LXB_CSS_TEXT_COMBINE_UPRIGHT_DIGITS = LXB_CSS_VALUE_DIGITS +}; +typedef unsigned int lxb_css_text_combine_upright_type_t; + + + + + +enum { + LXB_CSS_TEXT_DECORATION_LINE_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_TEXT_DECORATION_LINE_UNDERLINE = LXB_CSS_VALUE_UNDERLINE, + LXB_CSS_TEXT_DECORATION_LINE_OVERLINE = LXB_CSS_VALUE_OVERLINE, + LXB_CSS_TEXT_DECORATION_LINE_LINE_THROUGH = LXB_CSS_VALUE_LINE_THROUGH, + LXB_CSS_TEXT_DECORATION_LINE_BLINK = LXB_CSS_VALUE_BLINK +}; +typedef unsigned int lxb_css_text_decoration_line_type_t; + +enum { + LXB_CSS_TEXT_DECORATION_STYLE_SOLID = LXB_CSS_VALUE_SOLID, + LXB_CSS_TEXT_DECORATION_STYLE_DOUBLE = LXB_CSS_VALUE_DOUBLE, + LXB_CSS_TEXT_DECORATION_STYLE_DOTTED = LXB_CSS_VALUE_DOTTED, + LXB_CSS_TEXT_DECORATION_STYLE_DASHED = LXB_CSS_VALUE_DASHED, + LXB_CSS_TEXT_DECORATION_STYLE_WAVY = LXB_CSS_VALUE_WAVY +}; +typedef unsigned int lxb_css_text_decoration_style_type_t; + +enum { + LXB_CSS_TEXT_INDENT__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_TEXT_INDENT__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_TEXT_INDENT_HANGING = LXB_CSS_VALUE_HANGING, + LXB_CSS_TEXT_INDENT_EACH_LINE = LXB_CSS_VALUE_EACH_LINE +}; +typedef unsigned int lxb_css_text_indent_type_t; + +enum { + LXB_CSS_TEXT_JUSTIFY_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_TEXT_JUSTIFY_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_TEXT_JUSTIFY_INTER_WORD = LXB_CSS_VALUE_INTER_WORD, + LXB_CSS_TEXT_JUSTIFY_INTER_CHARACTER = LXB_CSS_VALUE_INTER_CHARACTER +}; +typedef unsigned int lxb_css_text_justify_type_t; + +enum { + LXB_CSS_TEXT_ORIENTATION_MIXED = LXB_CSS_VALUE_MIXED, + LXB_CSS_TEXT_ORIENTATION_UPRIGHT = LXB_CSS_VALUE_UPRIGHT, + LXB_CSS_TEXT_ORIENTATION_SIDEWAYS = LXB_CSS_VALUE_SIDEWAYS +}; +typedef unsigned int lxb_css_text_orientation_type_t; + +enum { + LXB_CSS_TEXT_OVERFLOW_CLIP = LXB_CSS_VALUE_CLIP, + LXB_CSS_TEXT_OVERFLOW_ELLIPSIS = LXB_CSS_VALUE_ELLIPSIS +}; +typedef unsigned int lxb_css_text_overflow_type_t; + +enum { + LXB_CSS_TEXT_TRANSFORM_NONE = LXB_CSS_VALUE_NONE, + LXB_CSS_TEXT_TRANSFORM_CAPITALIZE = LXB_CSS_VALUE_CAPITALIZE, + LXB_CSS_TEXT_TRANSFORM_UPPERCASE = LXB_CSS_VALUE_UPPERCASE, + LXB_CSS_TEXT_TRANSFORM_LOWERCASE = LXB_CSS_VALUE_LOWERCASE, + LXB_CSS_TEXT_TRANSFORM_FULL_WIDTH = LXB_CSS_VALUE_FULL_WIDTH, + LXB_CSS_TEXT_TRANSFORM_FULL_SIZE_KANA = LXB_CSS_VALUE_FULL_SIZE_KANA +}; +typedef unsigned int lxb_css_text_transform_type_t; + +enum { + LXB_CSS_TOP_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_TOP__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_TOP__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE +}; +typedef unsigned int lxb_css_top_type_t; + +enum { + LXB_CSS_UNICODE_BIDI_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_UNICODE_BIDI_EMBED = LXB_CSS_VALUE_EMBED, + LXB_CSS_UNICODE_BIDI_ISOLATE = LXB_CSS_VALUE_ISOLATE, + LXB_CSS_UNICODE_BIDI_BIDI_OVERRIDE = LXB_CSS_VALUE_BIDI_OVERRIDE, + LXB_CSS_UNICODE_BIDI_ISOLATE_OVERRIDE = LXB_CSS_VALUE_ISOLATE_OVERRIDE, + LXB_CSS_UNICODE_BIDI_PLAINTEXT = LXB_CSS_VALUE_PLAINTEXT +}; +typedef unsigned int lxb_css_unicode_bidi_type_t; + +enum { + LXB_CSS_VERTICAL_ALIGN_FIRST = LXB_CSS_VALUE_FIRST, + LXB_CSS_VERTICAL_ALIGN_LAST = LXB_CSS_VALUE_LAST +}; +typedef unsigned int lxb_css_vertical_align_type_t; + +enum { + LXB_CSS_VISIBILITY_VISIBLE = LXB_CSS_VALUE_VISIBLE, + LXB_CSS_VISIBILITY_HIDDEN = LXB_CSS_VALUE_HIDDEN, + LXB_CSS_VISIBILITY_COLLAPSE = LXB_CSS_VALUE_COLLAPSE +}; +typedef unsigned int lxb_css_visibility_type_t; + +enum { + LXB_CSS_WHITE_SPACE_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_WHITE_SPACE_PRE = LXB_CSS_VALUE_PRE, + LXB_CSS_WHITE_SPACE_NOWRAP = LXB_CSS_VALUE_NOWRAP, + LXB_CSS_WHITE_SPACE_PRE_WRAP = LXB_CSS_VALUE_PRE_WRAP, + LXB_CSS_WHITE_SPACE_BREAK_SPACES = LXB_CSS_VALUE_BREAK_SPACES, + LXB_CSS_WHITE_SPACE_PRE_LINE = LXB_CSS_VALUE_PRE_LINE +}; +typedef unsigned int lxb_css_white_space_type_t; + +enum { + LXB_CSS_WIDTH_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_WIDTH_MIN_CONTENT = LXB_CSS_VALUE_MIN_CONTENT, + LXB_CSS_WIDTH_MAX_CONTENT = LXB_CSS_VALUE_MAX_CONTENT, + LXB_CSS_WIDTH__LENGTH = LXB_CSS_VALUE__LENGTH, + LXB_CSS_WIDTH__PERCENTAGE = LXB_CSS_VALUE__PERCENTAGE, + LXB_CSS_WIDTH__NUMBER = LXB_CSS_VALUE__NUMBER, + LXB_CSS_WIDTH__ANGLE = LXB_CSS_VALUE__ANGLE +}; +typedef unsigned int lxb_css_width_type_t; + +enum { + LXB_CSS_WORD_BREAK_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_WORD_BREAK_KEEP_ALL = LXB_CSS_VALUE_KEEP_ALL, + LXB_CSS_WORD_BREAK_BREAK_ALL = LXB_CSS_VALUE_BREAK_ALL, + LXB_CSS_WORD_BREAK_BREAK_WORD = LXB_CSS_VALUE_BREAK_WORD +}; +typedef unsigned int lxb_css_word_break_type_t; + +enum { + LXB_CSS_WORD_SPACING_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_WORD_SPACING__LENGTH = LXB_CSS_VALUE__LENGTH +}; +typedef unsigned int lxb_css_word_spacing_type_t; + +enum { + LXB_CSS_WORD_WRAP_NORMAL = LXB_CSS_VALUE_NORMAL, + LXB_CSS_WORD_WRAP_BREAK_WORD = LXB_CSS_VALUE_BREAK_WORD, + LXB_CSS_WORD_WRAP_ANYWHERE = LXB_CSS_VALUE_ANYWHERE +}; +typedef unsigned int lxb_css_word_wrap_type_t; + +enum { + LXB_CSS_WRAP_FLOW_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_WRAP_FLOW_BOTH = LXB_CSS_VALUE_BOTH, + LXB_CSS_WRAP_FLOW_START = LXB_CSS_VALUE_START, + LXB_CSS_WRAP_FLOW_END = LXB_CSS_VALUE_END, + LXB_CSS_WRAP_FLOW_MINIMUM = LXB_CSS_VALUE_MINIMUM, + LXB_CSS_WRAP_FLOW_MAXIMUM = LXB_CSS_VALUE_MAXIMUM, + LXB_CSS_WRAP_FLOW_CLEAR = LXB_CSS_VALUE_CLEAR +}; +typedef unsigned int lxb_css_wrap_flow_type_t; + +enum { + LXB_CSS_WRAP_THROUGH_WRAP = LXB_CSS_VALUE_WRAP, + LXB_CSS_WRAP_THROUGH_NONE = LXB_CSS_VALUE_NONE +}; +typedef unsigned int lxb_css_wrap_through_type_t; + +enum { + LXB_CSS_WRITING_MODE_HORIZONTAL_TB = LXB_CSS_VALUE_HORIZONTAL_TB, + LXB_CSS_WRITING_MODE_VERTICAL_RL = LXB_CSS_VALUE_VERTICAL_RL, + LXB_CSS_WRITING_MODE_VERTICAL_LR = LXB_CSS_VALUE_VERTICAL_LR, + LXB_CSS_WRITING_MODE_SIDEWAYS_RL = LXB_CSS_VALUE_SIDEWAYS_RL, + LXB_CSS_WRITING_MODE_SIDEWAYS_LR = LXB_CSS_VALUE_SIDEWAYS_LR +}; +typedef unsigned int lxb_css_writing_mode_type_t; + +enum { + LXB_CSS_Z_INDEX_AUTO = LXB_CSS_VALUE_AUTO, + LXB_CSS_Z_INDEX__INTEGER = LXB_CSS_VALUE__INTEGER +}; +typedef unsigned int lxb_css_z_index_type_t; + + +#endif /* LXB_CSS_PROPERTY_CONST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/res.h new file mode 100644 index 0000000..eb5b903 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/res.h @@ -0,0 +1,524 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_PROPERTY_RES_H +#define LXB_CSS_PROPERTY_RES_H + +#include "lexbor/core/shs.h" +#include "lexbor/css/property/const.h" + + +static const lxb_css_entry_data_t lxb_css_property_data[LXB_CSS_PROPERTY__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_PROPERTY__UNDEF, lxb_css_property_state__undef, + lxb_css_property__undef_create, lxb_css_property__undef_destroy, lxb_css_property__undef_serialize, (void *) (uintptr_t) LXB_CSS_PROPERTY__UNDEF}, + {(lxb_char_t *) "#сustom", 7, LXB_CSS_PROPERTY__CUSTOM, lxb_css_property_state__custom, + lxb_css_property__custom_create, lxb_css_property__custom_destroy, lxb_css_property__custom_serialize, (void *) (uintptr_t) LXB_CSS_PROPERTY__CUSTOM}, + {(lxb_char_t *) "align-content", 13, LXB_CSS_PROPERTY_ALIGN_CONTENT, lxb_css_property_state_align_content, + lxb_css_property_align_content_create, lxb_css_property_align_content_destroy, lxb_css_property_align_content_serialize, + &(lxb_css_property_align_content_t) {.type = LXB_CSS_ALIGN_CONTENT_STRETCH}}, + {(lxb_char_t *) "align-items", 11, LXB_CSS_PROPERTY_ALIGN_ITEMS, lxb_css_property_state_align_items, + lxb_css_property_align_items_create, lxb_css_property_align_items_destroy, lxb_css_property_align_items_serialize, + &(lxb_css_property_align_items_t) {.type = LXB_CSS_ALIGN_ITEMS_STRETCH}}, + {(lxb_char_t *) "align-self", 10, LXB_CSS_PROPERTY_ALIGN_SELF, lxb_css_property_state_align_self, + lxb_css_property_align_self_create, lxb_css_property_align_self_destroy, lxb_css_property_align_self_serialize, + &(lxb_css_property_align_self_t) {.type = LXB_CSS_ALIGN_SELF_AUTO}}, + {(lxb_char_t *) "alignment-baseline", 18, LXB_CSS_PROPERTY_ALIGNMENT_BASELINE, lxb_css_property_state_alignment_baseline, + lxb_css_property_alignment_baseline_create, lxb_css_property_alignment_baseline_destroy, lxb_css_property_alignment_baseline_serialize, + &(lxb_css_property_alignment_baseline_t) {.type = LXB_CSS_ALIGNMENT_BASELINE_BASELINE}}, + {(lxb_char_t *) "background-color", 16, LXB_CSS_PROPERTY_BACKGROUND_COLOR, lxb_css_property_state_background_color, + lxb_css_property_background_color_create, lxb_css_property_background_color_destroy, lxb_css_property_background_color_serialize, + &(lxb_css_property_background_color_t) {.type = LXB_CSS_COLOR_TRANSPARENT}}, + {(lxb_char_t *) "baseline-shift", 14, LXB_CSS_PROPERTY_BASELINE_SHIFT, lxb_css_property_state_baseline_shift, + lxb_css_property_baseline_shift_create, lxb_css_property_baseline_shift_destroy, lxb_css_property_baseline_shift_serialize, + &(lxb_css_property_baseline_shift_t) {.type = LXB_CSS_VALUE__NUMBER, .u = {.length = {.num = 0, .is_float = false, .unit = LXB_CSS_UNIT__UNDEF}}}}, + {(lxb_char_t *) "baseline-source", 15, LXB_CSS_PROPERTY_BASELINE_SOURCE, lxb_css_property_state_baseline_source, + lxb_css_property_baseline_source_create, lxb_css_property_baseline_source_destroy, lxb_css_property_baseline_source_serialize, + &(lxb_css_property_baseline_source_t) {.type = LXB_CSS_BASELINE_SOURCE_AUTO}}, + {(lxb_char_t *) "border", 6, LXB_CSS_PROPERTY_BORDER, lxb_css_property_state_border, + lxb_css_property_border_create, lxb_css_property_border_destroy, lxb_css_property_border_serialize, + &(lxb_css_property_border_t) {.style = LXB_CSS_BORDER_NONE, .width = {.type = LXB_CSS_BORDER_MEDIUM}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "border-bottom", 13, LXB_CSS_PROPERTY_BORDER_BOTTOM, lxb_css_property_state_border_bottom, + lxb_css_property_border_bottom_create, lxb_css_property_border_bottom_destroy, lxb_css_property_border_bottom_serialize, + &(lxb_css_property_border_bottom_t) {.style = LXB_CSS_BORDER_NONE, .width = {.type = LXB_CSS_BORDER_MEDIUM}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "border-bottom-color", 19, LXB_CSS_PROPERTY_BORDER_BOTTOM_COLOR, lxb_css_property_state_border_bottom_color, + lxb_css_property_border_bottom_color_create, lxb_css_property_border_bottom_color_destroy, lxb_css_property_border_bottom_color_serialize, + &(lxb_css_property_border_bottom_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "border-left", 11, LXB_CSS_PROPERTY_BORDER_LEFT, lxb_css_property_state_border_left, + lxb_css_property_border_left_create, lxb_css_property_border_left_destroy, lxb_css_property_border_left_serialize, + &(lxb_css_property_border_left_t) {.style = LXB_CSS_BORDER_NONE, .width = {.type = LXB_CSS_BORDER_MEDIUM}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "border-left-color", 17, LXB_CSS_PROPERTY_BORDER_LEFT_COLOR, lxb_css_property_state_border_left_color, + lxb_css_property_border_left_color_create, lxb_css_property_border_left_color_destroy, lxb_css_property_border_left_color_serialize, + &(lxb_css_property_border_left_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "border-right", 12, LXB_CSS_PROPERTY_BORDER_RIGHT, lxb_css_property_state_border_right, + lxb_css_property_border_right_create, lxb_css_property_border_right_destroy, lxb_css_property_border_right_serialize, + &(lxb_css_property_border_right_t) {.style = LXB_CSS_BORDER_NONE, .width = {.type = LXB_CSS_BORDER_MEDIUM}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "border-right-color", 18, LXB_CSS_PROPERTY_BORDER_RIGHT_COLOR, lxb_css_property_state_border_right_color, + lxb_css_property_border_right_color_create, lxb_css_property_border_right_color_destroy, lxb_css_property_border_right_color_serialize, + &(lxb_css_property_border_right_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "border-top", 10, LXB_CSS_PROPERTY_BORDER_TOP, lxb_css_property_state_border_top, + lxb_css_property_border_top_create, lxb_css_property_border_top_destroy, lxb_css_property_border_top_serialize, + &(lxb_css_property_border_top_t) {.style = LXB_CSS_BORDER_NONE, .width = {.type = LXB_CSS_BORDER_MEDIUM}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "border-top-color", 16, LXB_CSS_PROPERTY_BORDER_TOP_COLOR, lxb_css_property_state_border_top_color, + lxb_css_property_border_top_color_create, lxb_css_property_border_top_color_destroy, lxb_css_property_border_top_color_serialize, + &(lxb_css_property_border_top_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "bottom", 6, LXB_CSS_PROPERTY_BOTTOM, lxb_css_property_state_bottom, + lxb_css_property_bottom_create, lxb_css_property_bottom_destroy, lxb_css_property_bottom_serialize, + &(lxb_css_property_bottom_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "box-sizing", 10, LXB_CSS_PROPERTY_BOX_SIZING, lxb_css_property_state_box_sizing, + lxb_css_property_box_sizing_create, lxb_css_property_box_sizing_destroy, lxb_css_property_box_sizing_serialize, + &(lxb_css_property_box_sizing_t) {.type = LXB_CSS_BOX_SIZING_CONTENT_BOX}}, + {(lxb_char_t *) "clear", 5, LXB_CSS_PROPERTY_CLEAR, lxb_css_property_state_clear, + lxb_css_property_clear_create, lxb_css_property_clear_destroy, lxb_css_property_clear_serialize, + &(lxb_css_property_clear_t) {.type = LXB_CSS_CLEAR_NONE}}, + {(lxb_char_t *) "color", 5, LXB_CSS_PROPERTY_COLOR, lxb_css_property_state_color, + lxb_css_property_color_create, lxb_css_property_color_destroy, lxb_css_property_color_serialize, + &(lxb_css_value_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "direction", 9, LXB_CSS_PROPERTY_DIRECTION, lxb_css_property_state_direction, + lxb_css_property_direction_create, lxb_css_property_direction_destroy, lxb_css_property_direction_serialize, + &(lxb_css_property_direction_t) {.type = LXB_CSS_DIRECTION_LTR}}, + {(lxb_char_t *) "display", 7, LXB_CSS_PROPERTY_DISPLAY, lxb_css_property_state_display, + lxb_css_property_display_create, lxb_css_property_display_destroy, lxb_css_property_display_serialize, + &(lxb_css_property_display_t) {.a = LXB_CSS_DISPLAY_INLINE, .b = LXB_CSS_PROPERTY__UNDEF, .c = LXB_CSS_PROPERTY__UNDEF}}, + {(lxb_char_t *) "dominant-baseline", 17, LXB_CSS_PROPERTY_DOMINANT_BASELINE, lxb_css_property_state_dominant_baseline, + lxb_css_property_dominant_baseline_create, lxb_css_property_dominant_baseline_destroy, lxb_css_property_dominant_baseline_serialize, + &(lxb_css_property_dominant_baseline_t) {.type = LXB_CSS_DOMINANT_BASELINE_AUTO}}, + {(lxb_char_t *) "flex", 4, LXB_CSS_PROPERTY_FLEX, lxb_css_property_state_flex, + lxb_css_property_flex_create, lxb_css_property_flex_destroy, lxb_css_property_flex_serialize, + &(lxb_css_property_flex_t) {.type = LXB_CSS_VALUE__UNDEF, .grow = {.type = LXB_CSS_FLEX_GROW__NUMBER, .number = {.num = 0, .is_float = false}}, .shrink = {.type = LXB_CSS_FLEX_SHRINK__NUMBER, .number = {.num = 1, .is_float = false}}, .basis = {.type = LXB_CSS_WIDTH_AUTO, .u.length = {.num = 0, .is_float = false}}}}, + {(lxb_char_t *) "flex-basis", 10, LXB_CSS_PROPERTY_FLEX_BASIS, lxb_css_property_state_flex_basis, + lxb_css_property_flex_basis_create, lxb_css_property_flex_basis_destroy, lxb_css_property_flex_basis_serialize, + &(lxb_css_property_flex_basis_t) {.type = LXB_CSS_WIDTH_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "flex-direction", 14, LXB_CSS_PROPERTY_FLEX_DIRECTION, lxb_css_property_state_flex_direction, + lxb_css_property_flex_direction_create, lxb_css_property_flex_direction_destroy, lxb_css_property_flex_direction_serialize, + &(lxb_css_property_flex_direction_t) {.type = LXB_CSS_FLEX_DIRECTION_ROW}}, + {(lxb_char_t *) "flex-flow", 9, LXB_CSS_PROPERTY_FLEX_FLOW, lxb_css_property_state_flex_flow, + lxb_css_property_flex_flow_create, lxb_css_property_flex_flow_destroy, lxb_css_property_flex_flow_serialize, + &(lxb_css_property_flex_flow_t) {.type_direction = LXB_CSS_FLEX_DIRECTION_ROW, .wrap = LXB_CSS_FLEX_WRAP_NOWRAP}}, + {(lxb_char_t *) "flex-grow", 9, LXB_CSS_PROPERTY_FLEX_GROW, lxb_css_property_state_flex_grow, + lxb_css_property_flex_grow_create, lxb_css_property_flex_grow_destroy, lxb_css_property_flex_grow_serialize, + &(lxb_css_property_flex_grow_t) {.type = LXB_CSS_FLEX_GROW__NUMBER, .number = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "flex-shrink", 11, LXB_CSS_PROPERTY_FLEX_SHRINK, lxb_css_property_state_flex_shrink, + lxb_css_property_flex_shrink_create, lxb_css_property_flex_shrink_destroy, lxb_css_property_flex_shrink_serialize, + &(lxb_css_property_flex_shrink_t) {.type = LXB_CSS_FLEX_SHRINK__NUMBER, .number = {.num = 1, .is_float = false}}}, + {(lxb_char_t *) "flex-wrap", 9, LXB_CSS_PROPERTY_FLEX_WRAP, lxb_css_property_state_flex_wrap, + lxb_css_property_flex_wrap_create, lxb_css_property_flex_wrap_destroy, lxb_css_property_flex_wrap_serialize, + &(lxb_css_property_flex_wrap_t) {.type = LXB_CSS_FLEX_WRAP_NOWRAP}}, + {(lxb_char_t *) "float", 5, LXB_CSS_PROPERTY_FLOAT, lxb_css_property_state_float, + lxb_css_property_float_create, lxb_css_property_float_destroy, lxb_css_property_float_serialize, + &(lxb_css_property_float_t) {.type = LXB_CSS_FLOAT_NONE}}, + {(lxb_char_t *) "float-defer", 11, LXB_CSS_PROPERTY_FLOAT_DEFER, lxb_css_property_state_float_defer, + lxb_css_property_float_defer_create, lxb_css_property_float_defer_destroy, lxb_css_property_float_defer_serialize, + &(lxb_css_property_float_defer_t) {.type = LXB_CSS_FLOAT_DEFER_NONE}}, + {(lxb_char_t *) "float-offset", 12, LXB_CSS_PROPERTY_FLOAT_OFFSET, lxb_css_property_state_float_offset, + lxb_css_property_float_offset_create, lxb_css_property_float_offset_destroy, lxb_css_property_float_offset_serialize, + &(lxb_css_property_float_offset_t) {.type = LXB_CSS_VALUE__NUMBER, .u = {.length = {.num = 0, .is_float = false, .unit = LXB_CSS_UNIT__UNDEF}}}}, + {(lxb_char_t *) "float-reference", 15, LXB_CSS_PROPERTY_FLOAT_REFERENCE, lxb_css_property_state_float_reference, + lxb_css_property_float_reference_create, lxb_css_property_float_reference_destroy, lxb_css_property_float_reference_serialize, + &(lxb_css_property_float_reference_t) {.type = LXB_CSS_FLOAT_REFERENCE_INLINE}}, + {(lxb_char_t *) "font-family", 11, LXB_CSS_PROPERTY_FONT_FAMILY, lxb_css_property_state_font_family, + lxb_css_property_font_family_create, lxb_css_property_font_family_destroy, lxb_css_property_font_family_serialize, + NULL}, + {(lxb_char_t *) "font-size", 9, LXB_CSS_PROPERTY_FONT_SIZE, lxb_css_property_state_font_size, + lxb_css_property_font_size_create, lxb_css_property_font_size_destroy, lxb_css_property_font_size_serialize, + &(lxb_css_property_font_size_t) {.type = LXB_CSS_FONT_SIZE_MEDIUM}}, + {(lxb_char_t *) "font-stretch", 12, LXB_CSS_PROPERTY_FONT_STRETCH, lxb_css_property_state_font_stretch, + lxb_css_property_font_stretch_create, lxb_css_property_font_stretch_destroy, lxb_css_property_font_stretch_serialize, + &(lxb_css_property_font_stretch_t) {.type = LXB_CSS_FONT_STRETCH_NORMAL}}, + {(lxb_char_t *) "font-style", 10, LXB_CSS_PROPERTY_FONT_STYLE, lxb_css_property_state_font_style, + lxb_css_property_font_style_create, lxb_css_property_font_style_destroy, lxb_css_property_font_style_serialize, + &(lxb_css_property_font_style_t) {.type = LXB_CSS_FONT_STYLE_NORMAL}}, + {(lxb_char_t *) "font-weight", 11, LXB_CSS_PROPERTY_FONT_WEIGHT, lxb_css_property_state_font_weight, + lxb_css_property_font_weight_create, lxb_css_property_font_weight_destroy, lxb_css_property_font_weight_serialize, + &(lxb_css_property_font_weight_t) {.type = LXB_CSS_FONT_WEIGHT_NORMAL}}, + {(lxb_char_t *) "hanging-punctuation", 19, LXB_CSS_PROPERTY_HANGING_PUNCTUATION, lxb_css_property_state_hanging_punctuation, + lxb_css_property_hanging_punctuation_create, lxb_css_property_hanging_punctuation_destroy, lxb_css_property_hanging_punctuation_serialize, + &(lxb_css_property_hanging_punctuation_t) {.type_first = LXB_CSS_HANGING_PUNCTUATION_NONE}}, + {(lxb_char_t *) "height", 6, LXB_CSS_PROPERTY_HEIGHT, lxb_css_property_state_height, + lxb_css_property_height_create, lxb_css_property_height_destroy, lxb_css_property_height_serialize, + &(lxb_css_property_height_t) {.type = LXB_CSS_HEIGHT_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "hyphens", 7, LXB_CSS_PROPERTY_HYPHENS, lxb_css_property_state_hyphens, + lxb_css_property_hyphens_create, lxb_css_property_hyphens_destroy, lxb_css_property_hyphens_serialize, + &(lxb_css_property_hyphens_t) {.type = LXB_CSS_HYPHENS_MANUAL}}, + {(lxb_char_t *) "inset-block-end", 15, LXB_CSS_PROPERTY_INSET_BLOCK_END, lxb_css_property_state_inset_block_end, + lxb_css_property_inset_block_end_create, lxb_css_property_inset_block_end_destroy, lxb_css_property_inset_block_end_serialize, + &(lxb_css_property_inset_block_end_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "inset-block-start", 17, LXB_CSS_PROPERTY_INSET_BLOCK_START, lxb_css_property_state_inset_block_start, + lxb_css_property_inset_block_start_create, lxb_css_property_inset_block_start_destroy, lxb_css_property_inset_block_start_serialize, + &(lxb_css_property_inset_block_start_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "inset-inline-end", 16, LXB_CSS_PROPERTY_INSET_INLINE_END, lxb_css_property_state_inset_inline_end, + lxb_css_property_inset_inline_end_create, lxb_css_property_inset_inline_end_destroy, lxb_css_property_inset_inline_end_serialize, + &(lxb_css_property_inset_inline_end_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "inset-inline-start", 18, LXB_CSS_PROPERTY_INSET_INLINE_START, lxb_css_property_state_inset_inline_start, + lxb_css_property_inset_inline_start_create, lxb_css_property_inset_inline_start_destroy, lxb_css_property_inset_inline_start_serialize, + &(lxb_css_property_inset_inline_start_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "justify-content", 15, LXB_CSS_PROPERTY_JUSTIFY_CONTENT, lxb_css_property_state_justify_content, + lxb_css_property_justify_content_create, lxb_css_property_justify_content_destroy, lxb_css_property_justify_content_serialize, + &(lxb_css_property_justify_content_t) {.type = LXB_CSS_JUSTIFY_CONTENT_FLEX_START}}, + {(lxb_char_t *) "left", 4, LXB_CSS_PROPERTY_LEFT, lxb_css_property_state_left, + lxb_css_property_left_create, lxb_css_property_left_destroy, lxb_css_property_left_serialize, + &(lxb_css_property_left_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "letter-spacing", 14, LXB_CSS_PROPERTY_LETTER_SPACING, lxb_css_property_state_letter_spacing, + lxb_css_property_letter_spacing_create, lxb_css_property_letter_spacing_destroy, lxb_css_property_letter_spacing_serialize, + &(lxb_css_property_letter_spacing_t) {.type = LXB_CSS_LETTER_SPACING_NORMAL}}, + {(lxb_char_t *) "line-break", 10, LXB_CSS_PROPERTY_LINE_BREAK, lxb_css_property_state_line_break, + lxb_css_property_line_break_create, lxb_css_property_line_break_destroy, lxb_css_property_line_break_serialize, + &(lxb_css_property_line_break_t) {.type = LXB_CSS_LINE_BREAK_AUTO}}, + {(lxb_char_t *) "line-height", 11, LXB_CSS_PROPERTY_LINE_HEIGHT, lxb_css_property_state_line_height, + lxb_css_property_line_height_create, lxb_css_property_line_height_destroy, lxb_css_property_line_height_serialize, + &(lxb_css_property_line_height_t) {.type = LXB_CSS_LINE_HEIGHT_NORMAL}}, + {(lxb_char_t *) "margin", 6, LXB_CSS_PROPERTY_MARGIN, lxb_css_property_state_margin, + lxb_css_property_margin_create, lxb_css_property_margin_destroy, lxb_css_property_margin_serialize, + &(lxb_css_property_margin_t) {.top = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .right = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .bottom = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .left = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}}, + {(lxb_char_t *) "margin-bottom", 13, LXB_CSS_PROPERTY_MARGIN_BOTTOM, lxb_css_property_state_margin_bottom, + lxb_css_property_margin_bottom_create, lxb_css_property_margin_bottom_destroy, lxb_css_property_margin_bottom_serialize, + &(lxb_css_property_margin_bottom_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "margin-left", 11, LXB_CSS_PROPERTY_MARGIN_LEFT, lxb_css_property_state_margin_left, + lxb_css_property_margin_left_create, lxb_css_property_margin_left_destroy, lxb_css_property_margin_left_serialize, + &(lxb_css_property_margin_left_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "margin-right", 12, LXB_CSS_PROPERTY_MARGIN_RIGHT, lxb_css_property_state_margin_right, + lxb_css_property_margin_right_create, lxb_css_property_margin_right_destroy, lxb_css_property_margin_right_serialize, + &(lxb_css_property_margin_right_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "margin-top", 10, LXB_CSS_PROPERTY_MARGIN_TOP, lxb_css_property_state_margin_top, + lxb_css_property_margin_top_create, lxb_css_property_margin_top_destroy, lxb_css_property_margin_top_serialize, + &(lxb_css_property_margin_top_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "max-height", 10, LXB_CSS_PROPERTY_MAX_HEIGHT, lxb_css_property_state_max_height, + lxb_css_property_max_height_create, lxb_css_property_max_height_destroy, lxb_css_property_max_height_serialize, + &(lxb_css_property_max_height_t) {.type = LXB_CSS_MAX_HEIGHT_NONE, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "max-width", 9, LXB_CSS_PROPERTY_MAX_WIDTH, lxb_css_property_state_max_width, + lxb_css_property_max_width_create, lxb_css_property_max_width_destroy, lxb_css_property_max_width_serialize, + &(lxb_css_property_max_width_t) {.type = LXB_CSS_MAX_WIDTH_NONE, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "min-height", 10, LXB_CSS_PROPERTY_MIN_HEIGHT, lxb_css_property_state_min_height, + lxb_css_property_min_height_create, lxb_css_property_min_height_destroy, lxb_css_property_min_height_serialize, + &(lxb_css_property_min_height_t) {.type = LXB_CSS_MIN_HEIGHT_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "min-width", 9, LXB_CSS_PROPERTY_MIN_WIDTH, lxb_css_property_state_min_width, + lxb_css_property_min_width_create, lxb_css_property_min_width_destroy, lxb_css_property_min_width_serialize, + &(lxb_css_property_min_width_t) {.type = LXB_CSS_MIN_WIDTH_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "opacity", 7, LXB_CSS_PROPERTY_OPACITY, lxb_css_property_state_opacity, + lxb_css_property_opacity_create, lxb_css_property_opacity_destroy, lxb_css_property_opacity_serialize, + &(lxb_css_property_opacity_t) {.type = LXB_CSS_OPACITY__NUMBER, .u = {.number = {.num = 1, .is_float = false}}}}, + {(lxb_char_t *) "order", 5, LXB_CSS_PROPERTY_ORDER, lxb_css_property_state_order, + lxb_css_property_order_create, lxb_css_property_order_destroy, lxb_css_property_order_serialize, + &(lxb_css_property_order_t) {.type = LXB_CSS_ORDER__INTEGER, .integer = {.num = 0}}}, + {(lxb_char_t *) "overflow-block", 14, LXB_CSS_PROPERTY_OVERFLOW_BLOCK, lxb_css_property_state_overflow_block, + lxb_css_property_overflow_block_create, lxb_css_property_overflow_block_destroy, lxb_css_property_overflow_block_serialize, + &(lxb_css_property_overflow_block_t) {.type = LXB_CSS_OVERFLOW_BLOCK_VISIBLE}}, + {(lxb_char_t *) "overflow-inline", 15, LXB_CSS_PROPERTY_OVERFLOW_INLINE, lxb_css_property_state_overflow_inline, + lxb_css_property_overflow_inline_create, lxb_css_property_overflow_inline_destroy, lxb_css_property_overflow_inline_serialize, + &(lxb_css_property_overflow_inline_t) {.type = LXB_CSS_OVERFLOW_INLINE_VISIBLE}}, + {(lxb_char_t *) "overflow-wrap", 13, LXB_CSS_PROPERTY_OVERFLOW_WRAP, lxb_css_property_state_overflow_wrap, + lxb_css_property_overflow_wrap_create, lxb_css_property_overflow_wrap_destroy, lxb_css_property_overflow_wrap_serialize, + &(lxb_css_property_overflow_wrap_t) {.type = LXB_CSS_OVERFLOW_WRAP_NORMAL}}, + {(lxb_char_t *) "overflow-x", 10, LXB_CSS_PROPERTY_OVERFLOW_X, lxb_css_property_state_overflow_x, + lxb_css_property_overflow_x_create, lxb_css_property_overflow_x_destroy, lxb_css_property_overflow_x_serialize, + &(lxb_css_property_overflow_x_t) {.type = LXB_CSS_OVERFLOW_X_VISIBLE}}, + {(lxb_char_t *) "overflow-y", 10, LXB_CSS_PROPERTY_OVERFLOW_Y, lxb_css_property_state_overflow_y, + lxb_css_property_overflow_y_create, lxb_css_property_overflow_y_destroy, lxb_css_property_overflow_y_serialize, + &(lxb_css_property_overflow_y_t) {.type = LXB_CSS_OVERFLOW_Y_VISIBLE}}, + {(lxb_char_t *) "padding", 7, LXB_CSS_PROPERTY_PADDING, lxb_css_property_state_padding, + lxb_css_property_padding_create, lxb_css_property_padding_destroy, lxb_css_property_padding_serialize, + &(lxb_css_property_padding_t) {.top = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .right = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .bottom = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}, .left = {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}}, + {(lxb_char_t *) "padding-bottom", 14, LXB_CSS_PROPERTY_PADDING_BOTTOM, lxb_css_property_state_padding_bottom, + lxb_css_property_padding_bottom_create, lxb_css_property_padding_bottom_destroy, lxb_css_property_padding_bottom_serialize, + &(lxb_css_property_padding_bottom_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "padding-left", 12, LXB_CSS_PROPERTY_PADDING_LEFT, lxb_css_property_state_padding_left, + lxb_css_property_padding_left_create, lxb_css_property_padding_left_destroy, lxb_css_property_padding_left_serialize, + &(lxb_css_property_padding_left_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "padding-right", 13, LXB_CSS_PROPERTY_PADDING_RIGHT, lxb_css_property_state_padding_right, + lxb_css_property_padding_right_create, lxb_css_property_padding_right_destroy, lxb_css_property_padding_right_serialize, + &(lxb_css_property_padding_right_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "padding-top", 11, LXB_CSS_PROPERTY_PADDING_TOP, lxb_css_property_state_padding_top, + lxb_css_property_padding_top_create, lxb_css_property_padding_top_destroy, lxb_css_property_padding_top_serialize, + &(lxb_css_property_padding_top_t) {.type = LXB_CSS_VALUE__LENGTH, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "position", 8, LXB_CSS_PROPERTY_POSITION, lxb_css_property_state_position, + lxb_css_property_position_create, lxb_css_property_position_destroy, lxb_css_property_position_serialize, + &(lxb_css_property_position_t) {.type = LXB_CSS_POSITION_STATIC}}, + {(lxb_char_t *) "right", 5, LXB_CSS_PROPERTY_RIGHT, lxb_css_property_state_right, + lxb_css_property_right_create, lxb_css_property_right_destroy, lxb_css_property_right_serialize, + &(lxb_css_property_right_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "tab-size", 8, LXB_CSS_PROPERTY_TAB_SIZE, lxb_css_property_state_tab_size, + lxb_css_property_tab_size_create, lxb_css_property_tab_size_destroy, lxb_css_property_tab_size_serialize, + &(lxb_css_property_tab_size_t) {.type = LXB_CSS_VALUE__NUMBER, .u = {.number = {.num = 8, .is_float = false}}}}, + {(lxb_char_t *) "text-align", 10, LXB_CSS_PROPERTY_TEXT_ALIGN, lxb_css_property_state_text_align, + lxb_css_property_text_align_create, lxb_css_property_text_align_destroy, lxb_css_property_text_align_serialize, + &(lxb_css_property_text_align_t) {.type = LXB_CSS_TEXT_ALIGN_START}}, + {(lxb_char_t *) "text-align-all", 14, LXB_CSS_PROPERTY_TEXT_ALIGN_ALL, lxb_css_property_state_text_align_all, + lxb_css_property_text_align_all_create, lxb_css_property_text_align_all_destroy, lxb_css_property_text_align_all_serialize, + &(lxb_css_property_text_align_all_t) {.type = LXB_CSS_TEXT_ALIGN_ALL_START}}, + {(lxb_char_t *) "text-align-last", 15, LXB_CSS_PROPERTY_TEXT_ALIGN_LAST, lxb_css_property_state_text_align_last, + lxb_css_property_text_align_last_create, lxb_css_property_text_align_last_destroy, lxb_css_property_text_align_last_serialize, + &(lxb_css_property_text_align_last_t) {.type = LXB_CSS_TEXT_ALIGN_LAST_AUTO}}, + {(lxb_char_t *) "text-combine-upright", 20, LXB_CSS_PROPERTY_TEXT_COMBINE_UPRIGHT, lxb_css_property_state_text_combine_upright, + lxb_css_property_text_combine_upright_create, lxb_css_property_text_combine_upright_destroy, lxb_css_property_text_combine_upright_serialize, + &(lxb_css_property_text_combine_upright_t) {.type = LXB_CSS_TEXT_COMBINE_UPRIGHT_NONE}}, + {(lxb_char_t *) "text-decoration", 15, LXB_CSS_PROPERTY_TEXT_DECORATION, lxb_css_property_state_text_decoration, + lxb_css_property_text_decoration_create, lxb_css_property_text_decoration_destroy, lxb_css_property_text_decoration_serialize, + &(lxb_css_property_text_decoration_t) {.line = {.type = LXB_CSS_TEXT_DECORATION_LINE_NONE}, .style = {.type = LXB_CSS_TEXT_DECORATION_STYLE_SOLID}, .color = {.type = LXB_CSS_COLOR_CURRENTCOLOR}}}, + {(lxb_char_t *) "text-decoration-color", 21, LXB_CSS_PROPERTY_TEXT_DECORATION_COLOR, lxb_css_property_state_text_decoration_color, + lxb_css_property_text_decoration_color_create, lxb_css_property_text_decoration_color_destroy, lxb_css_property_text_decoration_color_serialize, + &(lxb_css_property_text_decoration_color_t) {.type = LXB_CSS_COLOR_CURRENTCOLOR}}, + {(lxb_char_t *) "text-decoration-line", 20, LXB_CSS_PROPERTY_TEXT_DECORATION_LINE, lxb_css_property_state_text_decoration_line, + lxb_css_property_text_decoration_line_create, lxb_css_property_text_decoration_line_destroy, lxb_css_property_text_decoration_line_serialize, + &(lxb_css_property_text_decoration_line_t) {.type = LXB_CSS_TEXT_DECORATION_LINE_NONE}}, + {(lxb_char_t *) "text-decoration-style", 21, LXB_CSS_PROPERTY_TEXT_DECORATION_STYLE, lxb_css_property_state_text_decoration_style, + lxb_css_property_text_decoration_style_create, lxb_css_property_text_decoration_style_destroy, lxb_css_property_text_decoration_style_serialize, + &(lxb_css_property_text_decoration_style_t) {.type = LXB_CSS_TEXT_DECORATION_STYLE_SOLID}}, + {(lxb_char_t *) "text-indent", 11, LXB_CSS_PROPERTY_TEXT_INDENT, lxb_css_property_state_text_indent, + lxb_css_property_text_indent_create, lxb_css_property_text_indent_destroy, lxb_css_property_text_indent_serialize, + &(lxb_css_property_text_indent_t) {.length = {.type = LXB_CSS_VALUE__LENGTH, .u = {.length = {.num = 0, .is_float = false, .unit = LXB_CSS_UNIT__UNDEF}}}}}, + {(lxb_char_t *) "text-justify", 12, LXB_CSS_PROPERTY_TEXT_JUSTIFY, lxb_css_property_state_text_justify, + lxb_css_property_text_justify_create, lxb_css_property_text_justify_destroy, lxb_css_property_text_justify_serialize, + &(lxb_css_property_text_justify_t) {.type = LXB_CSS_TEXT_JUSTIFY_AUTO}}, + {(lxb_char_t *) "text-orientation", 16, LXB_CSS_PROPERTY_TEXT_ORIENTATION, lxb_css_property_state_text_orientation, + lxb_css_property_text_orientation_create, lxb_css_property_text_orientation_destroy, lxb_css_property_text_orientation_serialize, + &(lxb_css_property_text_orientation_t) {.type = LXB_CSS_TEXT_ORIENTATION_MIXED}}, + {(lxb_char_t *) "text-overflow", 13, LXB_CSS_PROPERTY_TEXT_OVERFLOW, lxb_css_property_state_text_overflow, + lxb_css_property_text_overflow_create, lxb_css_property_text_overflow_destroy, lxb_css_property_text_overflow_serialize, + &(lxb_css_property_text_overflow_t) {.type = LXB_CSS_TEXT_OVERFLOW_CLIP}}, + {(lxb_char_t *) "text-transform", 14, LXB_CSS_PROPERTY_TEXT_TRANSFORM, lxb_css_property_state_text_transform, + lxb_css_property_text_transform_create, lxb_css_property_text_transform_destroy, lxb_css_property_text_transform_serialize, + &(lxb_css_property_text_transform_t) {.type_case = LXB_CSS_TEXT_TRANSFORM_NONE, .full_width = LXB_CSS_PROPERTY__UNDEF, .full_size_kana = LXB_CSS_PROPERTY__UNDEF}}, + {(lxb_char_t *) "top", 3, LXB_CSS_PROPERTY_TOP, lxb_css_property_state_top, + lxb_css_property_top_create, lxb_css_property_top_destroy, lxb_css_property_top_serialize, + &(lxb_css_property_top_t) {.type = LXB_CSS_VALUE_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "unicode-bidi", 12, LXB_CSS_PROPERTY_UNICODE_BIDI, lxb_css_property_state_unicode_bidi, + lxb_css_property_unicode_bidi_create, lxb_css_property_unicode_bidi_destroy, lxb_css_property_unicode_bidi_serialize, + &(lxb_css_property_unicode_bidi_t) {.type = LXB_CSS_UNICODE_BIDI_NORMAL}}, + {(lxb_char_t *) "vertical-align", 14, LXB_CSS_PROPERTY_VERTICAL_ALIGN, lxb_css_property_state_vertical_align, + lxb_css_property_vertical_align_create, lxb_css_property_vertical_align_destroy, lxb_css_property_vertical_align_serialize, + &(lxb_css_property_vertical_align_t) {.type = LXB_CSS_ALIGNMENT_BASELINE_BASELINE}}, + {(lxb_char_t *) "visibility", 10, LXB_CSS_PROPERTY_VISIBILITY, lxb_css_property_state_visibility, + lxb_css_property_visibility_create, lxb_css_property_visibility_destroy, lxb_css_property_visibility_serialize, + &(lxb_css_property_visibility_t) {.type = LXB_CSS_VISIBILITY_VISIBLE}}, + {(lxb_char_t *) "white-space", 11, LXB_CSS_PROPERTY_WHITE_SPACE, lxb_css_property_state_white_space, + lxb_css_property_white_space_create, lxb_css_property_white_space_destroy, lxb_css_property_white_space_serialize, + &(lxb_css_property_white_space_t) {.type = LXB_CSS_WHITE_SPACE_NORMAL}}, + {(lxb_char_t *) "width", 5, LXB_CSS_PROPERTY_WIDTH, lxb_css_property_state_width, + lxb_css_property_width_create, lxb_css_property_width_destroy, lxb_css_property_width_serialize, + &(lxb_css_property_width_t) {.type = LXB_CSS_WIDTH_AUTO, .u.length = {.num = 0, .is_float = false}}}, + {(lxb_char_t *) "word-break", 10, LXB_CSS_PROPERTY_WORD_BREAK, lxb_css_property_state_word_break, + lxb_css_property_word_break_create, lxb_css_property_word_break_destroy, lxb_css_property_word_break_serialize, + &(lxb_css_property_word_break_t) {.type = LXB_CSS_WORD_BREAK_NORMAL}}, + {(lxb_char_t *) "word-spacing", 12, LXB_CSS_PROPERTY_WORD_SPACING, lxb_css_property_state_word_spacing, + lxb_css_property_word_spacing_create, lxb_css_property_word_spacing_destroy, lxb_css_property_word_spacing_serialize, + &(lxb_css_property_word_spacing_t) {.type = LXB_CSS_WORD_SPACING_NORMAL}}, + {(lxb_char_t *) "word-wrap", 9, LXB_CSS_PROPERTY_WORD_WRAP, lxb_css_property_state_word_wrap, + lxb_css_property_word_wrap_create, lxb_css_property_word_wrap_destroy, lxb_css_property_word_wrap_serialize, + &(lxb_css_property_word_wrap_t) {.type = LXB_CSS_WORD_WRAP_NORMAL}}, + {(lxb_char_t *) "wrap-flow", 9, LXB_CSS_PROPERTY_WRAP_FLOW, lxb_css_property_state_wrap_flow, + lxb_css_property_wrap_flow_create, lxb_css_property_wrap_flow_destroy, lxb_css_property_wrap_flow_serialize, + &(lxb_css_property_wrap_flow_t) {.type = LXB_CSS_WRAP_FLOW_AUTO}}, + {(lxb_char_t *) "wrap-through", 12, LXB_CSS_PROPERTY_WRAP_THROUGH, lxb_css_property_state_wrap_through, + lxb_css_property_wrap_through_create, lxb_css_property_wrap_through_destroy, lxb_css_property_wrap_through_serialize, + &(lxb_css_property_wrap_through_t) {.type = LXB_CSS_WRAP_THROUGH_WRAP}}, + {(lxb_char_t *) "writing-mode", 12, LXB_CSS_PROPERTY_WRITING_MODE, lxb_css_property_state_writing_mode, + lxb_css_property_writing_mode_create, lxb_css_property_writing_mode_destroy, lxb_css_property_writing_mode_serialize, + &(lxb_css_property_writing_mode_t) {.type = LXB_CSS_WRITING_MODE_HORIZONTAL_TB}}, + {(lxb_char_t *) "z-index", 7, LXB_CSS_PROPERTY_Z_INDEX, lxb_css_property_state_z_index, + lxb_css_property_z_index_create, lxb_css_property_z_index_destroy, lxb_css_property_z_index_serialize, + &(lxb_css_property_z_index_t) {.type = LXB_CSS_Z_INDEX_AUTO}} +}; + +static const lexbor_shs_entry_t lxb_css_property_shs[187] = +{ + {NULL, NULL, 186, 0}, + {"wrap-through", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WRAP_THROUGH], 12, 0}, + {"width", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WIDTH], 5, 0}, + {"color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_COLOR], 5, 0}, + {"text-overflow", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_OVERFLOW], 13, 0}, + {"text-combine-upright", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_COMBINE_UPRIGHT], 20, 0}, + {"direction", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_DIRECTION], 9, 0}, + {"baseline-source", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BASELINE_SOURCE], 15, 0}, + {"min-width", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MIN_WIDTH], 9, 0}, + {"wrap-flow", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WRAP_FLOW], 9, 1}, + {"align-content", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_ALIGN_CONTENT], 13, 0}, + {"padding-left", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_PADDING_LEFT], 12, 0}, + {"right", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_RIGHT], 5, 0}, + {"font-stretch", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FONT_STRETCH], 12, 0}, + {"flex-grow", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_GROW], 9, 0}, + {"border-top-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_TOP_COLOR], 16, 0}, + {"border-left-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_LEFT_COLOR], 17, 0}, + {"overflow-x", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OVERFLOW_X], 10, 0}, + {"clear", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_CLEAR], 5, 3}, + {"padding-top", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_PADDING_TOP], 11, 0}, + {"word-wrap", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WORD_WRAP], 9, 0}, + {"border-top", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_TOP], 10, 0}, + {"font-style", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FONT_STYLE], 10, 0}, + {"text-transform", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_TRANSFORM], 14, 0}, + {"white-space", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WHITE_SPACE], 11, 0}, + {"padding-bottom", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_PADDING_BOTTOM], 14, 0}, + {"vertical-align", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_VERTICAL_ALIGN], 14, 0}, + {"visibility", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_VISIBILITY], 10, 0}, + {"min-height", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MIN_HEIGHT], 10, 0}, + {"inset-inline-start", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_INSET_INLINE_START], 18, 0}, + {"flex-shrink", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_SHRINK], 11, 7}, + {NULL, NULL, 0, 0}, + {"overflow-wrap", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OVERFLOW_WRAP], 13, 0}, + {"letter-spacing", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_LETTER_SPACING], 14, 0}, + {NULL, NULL, 0, 0}, + {"bottom", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BOTTOM], 6, 0}, + {"max-width", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MAX_WIDTH], 9, 8}, + {NULL, NULL, 0, 0}, + {"dominant-baseline", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_DOMINANT_BASELINE], 17, 0}, + {"margin-top", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MARGIN_TOP], 10, 11}, + {"padding-right", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_PADDING_RIGHT], 13, 0}, + {NULL, NULL, 0, 0}, + {"border-bottom", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_BOTTOM], 13, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"text-align-all", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_ALIGN_ALL], 14, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"font-family", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FONT_FAMILY], 11, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"flex", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX], 4, 0}, + {"alignment-baseline", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_ALIGNMENT_BASELINE], 18, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"opacity", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OPACITY], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"overflow-y", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OVERFLOW_Y], 10, 0}, + {NULL, NULL, 0, 0}, + {"text-decoration-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_DECORATION_COLOR], 21, 0}, + {"left", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_LEFT], 4, 0}, + {"hyphens", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_HYPHENS], 7, 0}, + {"border", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER], 6, 13}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"flex-flow", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_FLOW], 9, 14}, + {NULL, NULL, 0, 0}, + {"line-height", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_LINE_HEIGHT], 11, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"box-sizing", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BOX_SIZING], 10, 0}, + {NULL, NULL, 0, 0}, + {"background-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BACKGROUND_COLOR], 16, 15}, + {"display", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_DISPLAY], 7, 16}, + {"border-right-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_RIGHT_COLOR], 18, 0}, + {"border-bottom-color", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_BOTTOM_COLOR], 19, 0}, + {"margin", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MARGIN], 6, 0}, + {"word-break", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WORD_BREAK], 10, 19}, + {NULL, NULL, 0, 0}, + {"padding", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_PADDING], 7, 0}, + {"align-items", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_ALIGN_ITEMS], 11, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"justify-content", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_JUSTIFY_CONTENT], 15, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"height", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_HEIGHT], 6, 0}, + {"inset-block-end", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_INSET_BLOCK_END], 15, 0}, + {"inset-inline-end", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_INSET_INLINE_END], 16, 0}, + {NULL, NULL, 0, 0}, + {"overflow-inline", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OVERFLOW_INLINE], 15, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"font-size", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FONT_SIZE], 9, 0}, + {"position", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_POSITION], 8, 22}, + {"float", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLOAT], 5, 0}, + {NULL, NULL, 0, 0}, + {"top", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TOP], 3, 0}, + {NULL, NULL, 0, 0}, + {"float-reference", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLOAT_REFERENCE], 15, 0}, + {NULL, NULL, 0, 0}, + {"font-weight", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FONT_WEIGHT], 11, 0}, + {"float-offset", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLOAT_OFFSET], 12, 23}, + {NULL, NULL, 0, 0}, + {"margin-bottom", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MARGIN_BOTTOM], 13, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"order", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_ORDER], 5, 0}, + {NULL, NULL, 0, 0}, + {"z-index", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_Z_INDEX], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"flex-basis", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_BASIS], 10, 24}, + {"writing-mode", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WRITING_MODE], 12, 0}, + {NULL, NULL, 0, 0}, + {"border-left", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_LEFT], 11, 0}, + {"border-right", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BORDER_RIGHT], 12, 0}, + {"hanging-punctuation", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_HANGING_PUNCTUATION], 19, 0}, + {"baseline-shift", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_BASELINE_SHIFT], 14, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"text-justify", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_JUSTIFY], 12, 26}, + {"float-defer", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLOAT_DEFER], 11, 0}, + {"max-height", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MAX_HEIGHT], 10, 28}, + {"margin-left", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MARGIN_LEFT], 11, 0}, + {"margin-right", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_MARGIN_RIGHT], 12, 0}, + {"unicode-bidi", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_UNICODE_BIDI], 12, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"tab-size", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TAB_SIZE], 8, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"flex-wrap", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_WRAP], 9, 0}, + {"align-self", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_ALIGN_SELF], 10, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"text-decoration-line", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_DECORATION_LINE], 20, 0}, + {"text-decoration-style", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_DECORATION_STYLE], 21, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"inset-block-start", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_INSET_BLOCK_START], 17, 0}, + {"text-align", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_ALIGN], 10, 29}, + {"word-spacing", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_WORD_SPACING], 12, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"text-decoration", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_DECORATION], 15, 0}, + {"text-orientation", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_ORIENTATION], 16, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"overflow-block", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_OVERFLOW_BLOCK], 14, 0}, + {NULL, NULL, 0, 0}, + {"text-indent", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_INDENT], 11, 0}, + {"flex-direction", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_FLEX_DIRECTION], 14, 0}, + {NULL, NULL, 0, 0}, + {"line-break", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_LINE_BREAK], 10, 0}, + {"text-align-last", (void *) &lxb_css_property_data[LXB_CSS_PROPERTY_TEXT_ALIGN_LAST], 15, 0} +}; + + +#endif /* LXB_CSS_PROPERTY_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.c new file mode 100644 index 0000000..198ef14 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.c @@ -0,0 +1,5343 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/property.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/rule.h" +#include "lexbor/css/value.h" +#include "lexbor/css/unit.h" +#include "lexbor/css/property/state.h" +#include "lexbor/css/property/res.h" + + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex[256]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_lowercase[256]; +#endif + +#include "lexbor/core/conv.h" + + +#define lxb_css_property_state_check_token(parser, token) \ + if ((token) == NULL) { \ + return lxb_css_parser_memory_fail(parser); \ + } + +#define lxb_css_property_state_get_type(parser, token, type) \ + do { \ + lxb_css_syntax_parser_consume(parser); \ + \ + token = lxb_css_syntax_parser_token_wo_ws(parser); \ + lxb_css_property_state_check_token(parser, token); \ + \ + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { \ + return lxb_css_parser_success(parser); \ + } \ + \ + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, \ + lxb_css_syntax_token_ident(token)->length); \ + } \ + while (false) + +#define LXB_CSS_PROPERTY_STATE_HEX_MASK(n) \ + ((((uint32_t) 1 << (32 - (n))) - 1) << (n)) + + +static bool +lxb_css_property_state_color_rgba_old(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_t *color); +static bool +lxb_css_property_state_color_hsla_old(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_hsla_t *hsl); + +static bool +lxb_css_property_state_length(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_length_t *length) +{ + const lxb_css_data_t *unit; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + unit = lxb_css_unit_absolute_relative_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + length->num = lxb_css_syntax_token_dimension(token)->num.num; + length->is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + length->unit = (lxb_css_unit_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (lxb_css_syntax_token_number(token)->num != 0) { + return false; + } + + length->num = lxb_css_syntax_token_number(token)->num; + length->is_float = lxb_css_syntax_token_number(token)->is_float; + length->unit = LXB_CSS_UNIT__UNDEF; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_length_percentage(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_length_percentage_t *lp) +{ + const lxb_css_data_t *unit; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + unit = lxb_css_unit_absolute_relative_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + lp->type = LXB_CSS_VALUE__LENGTH; + lp->u.length.num = lxb_css_syntax_token_dimension(token)->num.num; + lp->u.length.is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + lp->u.length.unit = (lxb_css_unit_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (lxb_css_syntax_token_number(token)->num != 0) { + return false; + } + + lp->type = LXB_CSS_VALUE__NUMBER; + lp->u.length.num = lxb_css_syntax_token_number(token)->num; + lp->u.length.is_float = lxb_css_syntax_token_number(token)->is_float; + lp->u.length.unit = LXB_CSS_UNIT__UNDEF; + break; + + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + lp->type = LXB_CSS_VALUE__PERCENTAGE; + lp->u.percentage.num = lxb_css_syntax_token_percentage(token)->num; + lp->u.percentage.is_float = lxb_css_syntax_token_percentage(token)->is_float; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_number_length_percentage(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_number_length_percentage_t *nlp) +{ + const lxb_css_data_t *unit; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + unit = lxb_css_unit_absolute_relative_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + nlp->type = LXB_CSS_VALUE__LENGTH; + nlp->u.length.num = lxb_css_syntax_token_dimension(token)->num.num; + nlp->u.length.is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + nlp->u.length.unit = (lxb_css_unit_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + nlp->type = LXB_CSS_VALUE__NUMBER; + nlp->u.number.num = lxb_css_syntax_token_number(token)->num; + nlp->u.number.is_float = lxb_css_syntax_token_number(token)->is_float; + break; + + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + nlp->type = LXB_CSS_VALUE__PERCENTAGE; + nlp->u.percentage.num = lxb_css_syntax_token_percentage(token)->num; + nlp->u.percentage.is_float = lxb_css_syntax_token_percentage(token)->is_float; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_number_length(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_number_length_t *nl) +{ + const lxb_css_data_t *unit; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + unit = lxb_css_unit_absolute_relative_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + nl->type = LXB_CSS_VALUE__LENGTH; + nl->u.length.num = lxb_css_syntax_token_dimension(token)->num.num; + nl->u.length.is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + nl->u.length.unit = (lxb_css_unit_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + nl->type = LXB_CSS_VALUE__NUMBER; + nl->u.number.num = lxb_css_syntax_token_number(token)->num; + nl->u.number.is_float = lxb_css_syntax_token_number(token)->is_float; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_number(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_number_t *number) +{ + if (token->type != LXB_CSS_SYNTAX_TOKEN_NUMBER) { + return false; + } + + number->num = lxb_css_syntax_token_number(token)->num; + number->is_float = lxb_css_syntax_token_number(token)->is_float; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_integer(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_integer_t *intg) +{ + long ln; + double num; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_NUMBER) { + return false; + } + + num = lxb_css_syntax_token_number(token)->num; + ln = lexbor_conv_double_to_long(num); + + num = num - (double) ln; + + if (num < 0.0 || num > 0.0) { + return false; + } + + intg->num = ln; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_percentage(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_percentage_t *perc) +{ + if (token->type != LXB_CSS_SYNTAX_TOKEN_PERCENTAGE) { + return false; + } + + perc->num = lxb_css_syntax_token_percentage(token)->num; + perc->is_float = lxb_css_syntax_token_percentage(token)->is_float; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_number_percentage_none(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_number_percentage_t *np) +{ + double num; + lxb_css_value_type_t type; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_NUMBER) { + np->type = LXB_CSS_VALUE__NUMBER; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_PERCENTAGE) { + np->type = LXB_CSS_VALUE__PERCENTAGE; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + if (type != LXB_CSS_VALUE_NONE) { + return false; + } + + np->type = LXB_CSS_VALUE_NONE; + + lxb_css_syntax_parser_consume(parser); + + return true; + } + else { + return false; + } + + num = lxb_css_syntax_token_number(token)->num; + + np->u.number.num = num; + np->u.number.is_float = lxb_css_syntax_token_number(token)->is_float; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_percentage_none(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_percentage_type_t *np) +{ + double num; + lxb_css_value_type_t type; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_PERCENTAGE) { + np->type = LXB_CSS_VALUE__PERCENTAGE; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + if (type != LXB_CSS_VALUE_NONE) { + return false; + } + + np->type = LXB_CSS_VALUE_NONE; + + lxb_css_syntax_parser_consume(parser); + + return true; + } + else { + return false; + } + + num = lxb_css_syntax_token_number(token)->num; + + np->percentage.num = num; + np->percentage.is_float = lxb_css_syntax_token_number(token)->is_float; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_number_percentage(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_number_percentage_t *np) +{ + double num; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_NUMBER) { + np->type = LXB_CSS_VALUE__NUMBER; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_PERCENTAGE) { + np->type = LXB_CSS_VALUE__PERCENTAGE; + } + else { + return false; + } + + num = lxb_css_syntax_token_number(token)->num; + + np->u.number.num = num; + np->u.number.is_float = lxb_css_syntax_token_number(token)->is_float; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_angle(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_angle_t *angle) +{ + const lxb_css_data_t *unit; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_DIMENSION) { + return false; + } + + unit = lxb_css_unit_angle_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + angle->num = lxb_css_syntax_token_dimension(token)->num.num; + angle->is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + angle->unit = (lxb_css_unit_angle_t) unit->unique; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +bool +lxb_css_property_state_width_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_width_t *width) +{ + lxb_css_value_type_t type; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_AUTO: + case LXB_CSS_VALUE_MIN_CONTENT: + case LXB_CSS_VALUE_MAX_CONTENT: + width->type = type; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; + } + + return lxb_css_property_state_length_percentage(parser, token, + (lxb_css_value_length_percentage_t *)width); +} + +static bool +lxb_css_property_state_hue(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_hue_t *hue) +{ + const lxb_css_data_t *unit; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + unit = lxb_css_unit_angle_by_name(lxb_css_syntax_token_dimension(token)->str.data, + lxb_css_syntax_token_dimension(token)->str.length); + if (unit == NULL) { + return false; + } + + hue->type = LXB_CSS_VALUE__ANGLE; + hue->u.angle.num = lxb_css_syntax_token_dimension(token)->num.num; + hue->u.angle.is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + hue->u.angle.unit = (lxb_css_unit_angle_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + hue->type = LXB_CSS_VALUE__NUMBER; + hue->u.number.num = lxb_css_syntax_token_number(token)->num; + hue->u.number.is_float = lxb_css_syntax_token_number(token)->is_float; + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +lxb_inline bool +lxb_css_property_state_hue_none(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_hue_t *hue) +{ + lxb_css_value_type_t type; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_property_state_hue(parser, token, hue); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + if (type != LXB_CSS_VALUE_NONE) { + return false; + } + + hue->type = LXB_CSS_VALUE_NONE; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_hex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_t *color) +{ + size_t length; + uint32_t chex; + lxb_char_t ch; + const lxb_char_t *end, *p; + lxb_css_value_color_hex_rgba_t *rgba; + + length = token->types.hash.length; + + if (length > 8) { + return false; + } + + p = token->types.hash.data; + end = p + length; + + chex = 0; + + while (p < end) { + ch = lexbor_str_res_map_lowercase[lexbor_str_res_map_hex[*p]]; + + if (ch == 0xff) { + return false; + } + + chex = chex << 4 | ch; + + p++; + } + + rgba = &color->u.hex.rgba; + + switch (length) { + case 3: + rgba->r = chex >> 8; + rgba->g = chex >> 4 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(4); + rgba->b = chex & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(4); + rgba->a = 0xff; + + color->u.hex.type = LXB_CSS_PROPERTY_COLOR_HEX_TYPE_3; + break; + + case 4: + rgba->r = chex >> 12; + rgba->g = chex >> 8 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(4); + rgba->b = chex >> 4 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(4); + rgba->a = chex & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(4); + + color->u.hex.type = LXB_CSS_PROPERTY_COLOR_HEX_TYPE_4; + break; + + case 6: + rgba->r = chex >> 16; + rgba->g = chex >> 8 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(8); + rgba->b = chex & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(8); + rgba->a = 0xff; + + color->u.hex.type = LXB_CSS_PROPERTY_COLOR_HEX_TYPE_6; + break; + + case 8: + rgba->r = chex >> 24; + rgba->g = chex >> 16 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(8); + rgba->b = chex >> 8 & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(8); + rgba->a = chex & ~LXB_CSS_PROPERTY_STATE_HEX_MASK(8); + + color->u.hex.type = LXB_CSS_PROPERTY_COLOR_HEX_TYPE_8; + break; + + default: + return false; + } + + color->type = LXB_CSS_COLOR_HEX; + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_rgba(lxb_css_parser_t *parser, + lxb_css_value_color_t *color) +{ + bool res; + lxb_css_color_type_t type; + lxb_css_value_color_rgba_t *rgb; + const lxb_css_syntax_token_t *token; + + rgb = &color->u.rgb; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &rgb->r); + if (res == false) { + return false; + } + + type = rgb->r.type; + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_COMMA) { + /* Deprecated format. */ + + if (type == LXB_CSS_VALUE_NONE) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + rgb->old = true; + + return lxb_css_property_state_color_rgba_old(parser, token, color); + } + + res = lxb_css_property_state_number_percentage_none(parser, token, &rgb->g); + if (res == false) { + return false; + } + + if (type != rgb->g.type) { + if (type == LXB_CSS_VALUE_NONE) { + type = rgb->g.type; + } + else if (rgb->g.type != LXB_CSS_VALUE_NONE) { + return false; + } + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &rgb->b); + if (res == false) { + return false; + } + + if (type != rgb->b.type && type != LXB_CSS_VALUE_NONE + && rgb->b.type != LXB_CSS_VALUE_NONE) + { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + if (lxb_css_syntax_token_delim(token)->character != '/') { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + return true; + } + else { + return false; + } + + res = lxb_css_property_state_number_percentage_none(parser, token, &rgb->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_rgba_old(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_t *color) +{ + bool res; + lxb_css_value_color_rgba_t *rgb; + + rgb = &color->u.rgb; + + res = lxb_css_property_state_number_percentage(parser, token, &rgb->g); + if (res == false) { + return false; + } + + if (rgb->r.type != rgb->g.type) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage(parser, token, &rgb->b); + if (res == false) { + return false; + } + + if (rgb->r.type != rgb->b.type) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + return true; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage(parser, token, &rgb->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_hsla(lxb_css_parser_t *parser, + lxb_css_value_color_t *color) +{ + bool res; + lxb_css_value_color_hsla_t *hsl; + const lxb_css_syntax_token_t *token; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + hsl = &color->u.hsl; + + res = lxb_css_property_state_hue_none(parser, token, &hsl->h); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_COMMA) { + /* Deprecated format. */ + + if (hsl->h.type == LXB_CSS_VALUE_NONE) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + hsl->old = true; + + return lxb_css_property_state_color_hsla_old(parser, token, hsl); + } + + res = lxb_css_property_state_percentage_none(parser, token, &hsl->s); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_percentage_none(parser, token, &hsl->l); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + if (lxb_css_syntax_token_delim(token)->character != '/') { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + return true; + } + + res = lxb_css_property_state_number_percentage_none(parser, token, &hsl->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_hsla_old(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_hsla_t *hsl) +{ + bool res; + + res = lxb_css_property_state_percentage(parser, token, &hsl->s.percentage); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_percentage(parser, token, &hsl->l.percentage); + if (res == false) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + goto done; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage(parser, token, &hsl->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + +done: + + lxb_css_syntax_parser_consume(parser); + + hsl->s.type = LXB_CSS_VALUE__PERCENTAGE; + hsl->l.type = LXB_CSS_VALUE__PERCENTAGE; + + return true; +} + +static bool +lxb_css_property_state_color_lab(lxb_css_parser_t *parser, + lxb_css_value_color_t *color) +{ + bool res; + lxb_css_value_color_lab_t *lab; + const lxb_css_syntax_token_t *token; + + lab = &color->u.lab; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &lab->l); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &lab->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &lab->b); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + if (lxb_css_syntax_token_delim(token)->character != '/') { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + return true; + } + else { + return false; + } + + res = lxb_css_property_state_number_percentage_none(parser, token, + &lab->alpha); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +static bool +lxb_css_property_state_color_lch(lxb_css_parser_t *parser, + lxb_css_value_color_t *color) +{ + bool res; + lxb_css_value_color_lch_t *lch; + const lxb_css_syntax_token_t *token; + + lch = &color->u.lch; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &lch->l); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number_percentage_none(parser, token, &lch->c); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_hue_none(parser, token, &lch->h); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + if (lxb_css_syntax_token_delim(token)->character != '/') { + return false; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + return true; + } + else { + return false; + } + + res = lxb_css_property_state_number_percentage_none(parser, token, &lch->a); + if (res == false) { + return false; + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +/* + * Return: + * true and status always LXB_STATUS_OK — token consumed, ok. + * false and status != LXB_STATUS_OK — token consumed, not ok. + * false and status == LXB_STATUS_OK — token not consumed, not ok. + */ +static bool +lxb_css_property_state_color_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_value_color_t *color, + lxb_status_t *status) +{ + bool res; + lxb_css_value_type_t type; + + *status = LXB_STATUS_OK; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + color->type = LXB_CSS_VALUE_HEX; + + return lxb_css_property_state_color_hex(parser, token, color); + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + type = lxb_css_value_by_name(lxb_css_syntax_token_function(token)->data, + lxb_css_syntax_token_function(token)->length); + color->type = type; + + switch (type) { + /* */ + case LXB_CSS_VALUE_RGB: + case LXB_CSS_VALUE_RGBA: + res = lxb_css_property_state_color_rgba(parser, color); + break; + + case LXB_CSS_VALUE_HSL: + case LXB_CSS_VALUE_HSLA: + case LXB_CSS_VALUE_HWB: + res = lxb_css_property_state_color_hsla(parser, color); + break; + + case LXB_CSS_VALUE_LAB: + case LXB_CSS_VALUE_OKLAB: + res = lxb_css_property_state_color_lab(parser, color); + break; + + case LXB_CSS_VALUE_LCH: + case LXB_CSS_VALUE_OKLCH: + res = lxb_css_property_state_color_lch(parser, color); + break; + + case LXB_CSS_VALUE_COLOR: + default: + *status = LXB_STATUS_OK; + return false; + } + + if (!res) { + *status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return res; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* */ + case LXB_CSS_VALUE_CURRENTCOLOR: + /* */ + case LXB_CSS_VALUE_CANVAS: + case LXB_CSS_VALUE_CANVASTEXT: + case LXB_CSS_VALUE_LINKTEXT: + case LXB_CSS_VALUE_VISITEDTEXT: + case LXB_CSS_VALUE_ACTIVETEXT: + case LXB_CSS_VALUE_BUTTONFACE: + case LXB_CSS_VALUE_BUTTONTEXT: + case LXB_CSS_VALUE_BUTTONBORDER: + case LXB_CSS_VALUE_FIELD: + case LXB_CSS_VALUE_FIELDTEXT: + case LXB_CSS_VALUE_HIGHLIGHT: + case LXB_CSS_VALUE_HIGHLIGHTTEXT: + case LXB_CSS_VALUE_SELECTEDITEM: + case LXB_CSS_VALUE_SELECTEDITEMTEXT: + case LXB_CSS_VALUE_MARK: + case LXB_CSS_VALUE_MARKTEXT: + case LXB_CSS_VALUE_GRAYTEXT: + case LXB_CSS_VALUE_ACCENTCOLOR: + case LXB_CSS_VALUE_ACCENTCOLORTEXT: + /* */ + case LXB_CSS_VALUE_TRANSPARENT: + /* */ + case LXB_CSS_VALUE_ALICEBLUE: + case LXB_CSS_VALUE_ANTIQUEWHITE: + case LXB_CSS_VALUE_AQUA: + case LXB_CSS_VALUE_AQUAMARINE: + case LXB_CSS_VALUE_AZURE: + case LXB_CSS_VALUE_BEIGE: + case LXB_CSS_VALUE_BISQUE: + case LXB_CSS_VALUE_BLACK: + case LXB_CSS_VALUE_BLANCHEDALMOND: + case LXB_CSS_VALUE_BLUE: + case LXB_CSS_VALUE_BLUEVIOLET: + case LXB_CSS_VALUE_BROWN: + case LXB_CSS_VALUE_BURLYWOOD: + case LXB_CSS_VALUE_CADETBLUE: + case LXB_CSS_VALUE_CHARTREUSE: + case LXB_CSS_VALUE_CHOCOLATE: + case LXB_CSS_VALUE_CORAL: + case LXB_CSS_VALUE_CORNFLOWERBLUE: + case LXB_CSS_VALUE_CORNSILK: + case LXB_CSS_VALUE_CRIMSON: + case LXB_CSS_VALUE_CYAN: + case LXB_CSS_VALUE_DARKBLUE: + case LXB_CSS_VALUE_DARKCYAN: + case LXB_CSS_VALUE_DARKGOLDENROD: + case LXB_CSS_VALUE_DARKGRAY: + case LXB_CSS_VALUE_DARKGREEN: + case LXB_CSS_VALUE_DARKGREY: + case LXB_CSS_VALUE_DARKKHAKI: + case LXB_CSS_VALUE_DARKMAGENTA: + case LXB_CSS_VALUE_DARKOLIVEGREEN: + case LXB_CSS_VALUE_DARKORANGE: + case LXB_CSS_VALUE_DARKORCHID: + case LXB_CSS_VALUE_DARKRED: + case LXB_CSS_VALUE_DARKSALMON: + case LXB_CSS_VALUE_DARKSEAGREEN: + case LXB_CSS_VALUE_DARKSLATEBLUE: + case LXB_CSS_VALUE_DARKSLATEGRAY: + case LXB_CSS_VALUE_DARKSLATEGREY: + case LXB_CSS_VALUE_DARKTURQUOISE: + case LXB_CSS_VALUE_DARKVIOLET: + case LXB_CSS_VALUE_DEEPPINK: + case LXB_CSS_VALUE_DEEPSKYBLUE: + case LXB_CSS_VALUE_DIMGRAY: + case LXB_CSS_VALUE_DIMGREY: + case LXB_CSS_VALUE_DODGERBLUE: + case LXB_CSS_VALUE_FIREBRICK: + case LXB_CSS_VALUE_FLORALWHITE: + case LXB_CSS_VALUE_FORESTGREEN: + case LXB_CSS_VALUE_FUCHSIA: + case LXB_CSS_VALUE_GAINSBORO: + case LXB_CSS_VALUE_GHOSTWHITE: + case LXB_CSS_VALUE_GOLD: + case LXB_CSS_VALUE_GOLDENROD: + case LXB_CSS_VALUE_GRAY: + case LXB_CSS_VALUE_GREEN: + case LXB_CSS_VALUE_GREENYELLOW: + case LXB_CSS_VALUE_GREY: + case LXB_CSS_VALUE_HONEYDEW: + case LXB_CSS_VALUE_HOTPINK: + case LXB_CSS_VALUE_INDIANRED: + case LXB_CSS_VALUE_INDIGO: + case LXB_CSS_VALUE_IVORY: + case LXB_CSS_VALUE_KHAKI: + case LXB_CSS_VALUE_LAVENDER: + case LXB_CSS_VALUE_LAVENDERBLUSH: + case LXB_CSS_VALUE_LAWNGREEN: + case LXB_CSS_VALUE_LEMONCHIFFON: + case LXB_CSS_VALUE_LIGHTBLUE: + case LXB_CSS_VALUE_LIGHTCORAL: + case LXB_CSS_VALUE_LIGHTCYAN: + case LXB_CSS_VALUE_LIGHTGOLDENRODYELLOW: + case LXB_CSS_VALUE_LIGHTGRAY: + case LXB_CSS_VALUE_LIGHTGREEN: + case LXB_CSS_VALUE_LIGHTGREY: + case LXB_CSS_VALUE_LIGHTPINK: + case LXB_CSS_VALUE_LIGHTSALMON: + case LXB_CSS_VALUE_LIGHTSEAGREEN: + case LXB_CSS_VALUE_LIGHTSKYBLUE: + case LXB_CSS_VALUE_LIGHTSLATEGRAY: + case LXB_CSS_VALUE_LIGHTSLATEGREY: + case LXB_CSS_VALUE_LIGHTSTEELBLUE: + case LXB_CSS_VALUE_LIGHTYELLOW: + case LXB_CSS_VALUE_LIME: + case LXB_CSS_VALUE_LIMEGREEN: + case LXB_CSS_VALUE_LINEN: + case LXB_CSS_VALUE_MAGENTA: + case LXB_CSS_VALUE_MAROON: + case LXB_CSS_VALUE_MEDIUMAQUAMARINE: + case LXB_CSS_VALUE_MEDIUMBLUE: + case LXB_CSS_VALUE_MEDIUMORCHID: + case LXB_CSS_VALUE_MEDIUMPURPLE: + case LXB_CSS_VALUE_MEDIUMSEAGREEN: + case LXB_CSS_VALUE_MEDIUMSLATEBLUE: + case LXB_CSS_VALUE_MEDIUMSPRINGGREEN: + case LXB_CSS_VALUE_MEDIUMTURQUOISE: + case LXB_CSS_VALUE_MEDIUMVIOLETRED: + case LXB_CSS_VALUE_MIDNIGHTBLUE: + case LXB_CSS_VALUE_MINTCREAM: + case LXB_CSS_VALUE_MISTYROSE: + case LXB_CSS_VALUE_MOCCASIN: + case LXB_CSS_VALUE_NAVAJOWHITE: + case LXB_CSS_VALUE_NAVY: + case LXB_CSS_VALUE_OLDLACE: + case LXB_CSS_VALUE_OLIVE: + case LXB_CSS_VALUE_OLIVEDRAB: + case LXB_CSS_VALUE_ORANGE: + case LXB_CSS_VALUE_ORANGERED: + case LXB_CSS_VALUE_ORCHID: + case LXB_CSS_VALUE_PALEGOLDENROD: + case LXB_CSS_VALUE_PALEGREEN: + case LXB_CSS_VALUE_PALETURQUOISE: + case LXB_CSS_VALUE_PALEVIOLETRED: + case LXB_CSS_VALUE_PAPAYAWHIP: + case LXB_CSS_VALUE_PEACHPUFF: + case LXB_CSS_VALUE_PERU: + case LXB_CSS_VALUE_PINK: + case LXB_CSS_VALUE_PLUM: + case LXB_CSS_VALUE_POWDERBLUE: + case LXB_CSS_VALUE_PURPLE: + case LXB_CSS_VALUE_REBECCAPURPLE: + case LXB_CSS_VALUE_RED: + case LXB_CSS_VALUE_ROSYBROWN: + case LXB_CSS_VALUE_ROYALBLUE: + case LXB_CSS_VALUE_SADDLEBROWN: + case LXB_CSS_VALUE_SALMON: + case LXB_CSS_VALUE_SANDYBROWN: + case LXB_CSS_VALUE_SEAGREEN: + case LXB_CSS_VALUE_SEASHELL: + case LXB_CSS_VALUE_SIENNA: + case LXB_CSS_VALUE_SILVER: + case LXB_CSS_VALUE_SKYBLUE: + case LXB_CSS_VALUE_SLATEBLUE: + case LXB_CSS_VALUE_SLATEGRAY: + case LXB_CSS_VALUE_SLATEGREY: + case LXB_CSS_VALUE_SNOW: + case LXB_CSS_VALUE_SPRINGGREEN: + case LXB_CSS_VALUE_STEELBLUE: + case LXB_CSS_VALUE_TAN: + case LXB_CSS_VALUE_TEAL: + case LXB_CSS_VALUE_THISTLE: + case LXB_CSS_VALUE_TOMATO: + case LXB_CSS_VALUE_TURQUOISE: + case LXB_CSS_VALUE_VIOLET: + case LXB_CSS_VALUE_WHEAT: + case LXB_CSS_VALUE_WHITE: + case LXB_CSS_VALUE_WHITESMOKE: + case LXB_CSS_VALUE_YELLOW: + case LXB_CSS_VALUE_YELLOWGREEN: + color->type = type; + break; + + default: + return false; + } + + break; + + default: + return false; + } + + lxb_css_syntax_parser_consume(parser); + + return true; +} + +bool +lxb_css_property_state__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_failed(parser); +} + +bool +lxb_css_property_state__custom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_status_t status; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property__custom_t *custom = declar->u.custom; + + (void) lexbor_str_init(&custom->value, parser->memory->mraw, 0); + if (custom->value.data == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + status = lxb_css_syntax_token_serialize_str(token, &custom->value, + parser->memory->mraw); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail(parser); + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_display(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_property_display_t *display; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + display = declar->u.display; + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_BLOCK: + case LXB_CSS_DISPLAY_INLINE: + case LXB_CSS_DISPLAY_RUN_IN: + display->a = type; + goto inside_listitem; + + /* */ + case LXB_CSS_DISPLAY_FLOW: + case LXB_CSS_DISPLAY_FLOW_ROOT: + display->a = type; + goto outside_listitem; + + case LXB_CSS_DISPLAY_TABLE: + case LXB_CSS_DISPLAY_FLEX: + case LXB_CSS_DISPLAY_GRID: + case LXB_CSS_DISPLAY_RUBY: + display->a = type; + goto outside; + + /* */ + case LXB_CSS_DISPLAY_LIST_ITEM: + display->a = type; + goto listitem_only; + + /* */ + case LXB_CSS_DISPLAY_TABLE_ROW_GROUP: + case LXB_CSS_DISPLAY_TABLE_HEADER_GROUP: + case LXB_CSS_DISPLAY_TABLE_FOOTER_GROUP: + case LXB_CSS_DISPLAY_TABLE_ROW: + case LXB_CSS_DISPLAY_TABLE_CELL: + case LXB_CSS_DISPLAY_TABLE_COLUMN_GROUP: + case LXB_CSS_DISPLAY_TABLE_COLUMN: + case LXB_CSS_DISPLAY_TABLE_CAPTION: + case LXB_CSS_DISPLAY_RUBY_BASE: + case LXB_CSS_DISPLAY_RUBY_TEXT: + case LXB_CSS_DISPLAY_RUBY_BASE_CONTAINER: + case LXB_CSS_DISPLAY_RUBY_TEXT_CONTAINER: + /* */ + case LXB_CSS_DISPLAY_CONTENTS: + case LXB_CSS_DISPLAY_NONE: + /* */ + case LXB_CSS_DISPLAY_INLINE_BLOCK: + case LXB_CSS_DISPLAY_INLINE_TABLE: + case LXB_CSS_DISPLAY_INLINE_FLEX: + case LXB_CSS_DISPLAY_INLINE_GRID: + display->a = type; + goto done; + + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + display->a = type; + goto done; + + default: + return lxb_css_parser_failed(parser); + } + +inside_listitem: + + lxb_css_property_state_get_type(parser, token, type); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_FLOW: + case LXB_CSS_DISPLAY_FLOW_ROOT: + display->b = type; + break; + + case LXB_CSS_DISPLAY_TABLE: + case LXB_CSS_DISPLAY_FLEX: + case LXB_CSS_DISPLAY_GRID: + case LXB_CSS_DISPLAY_RUBY: + display->b = type; + goto done; + + case LXB_CSS_DISPLAY_LIST_ITEM: + display->b = type; + goto flow_only; + + default: + return lxb_css_parser_failed(parser); + } + +listitem: + + lxb_css_property_state_get_type(parser, token, type); + + if (type == LXB_CSS_DISPLAY_LIST_ITEM) { + display->c = type; + goto done; + } + + return lxb_css_parser_failed(parser); + +outside: + + lxb_css_property_state_get_type(parser, token, type); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_BLOCK: + case LXB_CSS_DISPLAY_INLINE: + case LXB_CSS_DISPLAY_RUN_IN: + if (display->b == LXB_CSS_PROPERTY__UNDEF) { + display->b = type; + } + else { + display->c = type; + } + + goto done; + + default: + return lxb_css_parser_failed(parser); + } + +outside_listitem: + + lxb_css_property_state_get_type(parser, token, type); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_BLOCK: + case LXB_CSS_DISPLAY_INLINE: + case LXB_CSS_DISPLAY_RUN_IN: + display->b = type; + goto listitem; + + case LXB_CSS_DISPLAY_LIST_ITEM: + display->b = type; + goto outside; + + default: + return lxb_css_parser_failed(parser); + } + +listitem_only: + + lxb_css_property_state_get_type(parser, token, type); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_BLOCK: + case LXB_CSS_DISPLAY_INLINE: + case LXB_CSS_DISPLAY_RUN_IN: + display->b = type; + break; + + /* */ + case LXB_CSS_DISPLAY_FLOW: + case LXB_CSS_DISPLAY_FLOW_ROOT: + display->b = type; + goto outside; + + default: + return lxb_css_parser_failed(parser); + } + +flow_only: + + lxb_css_property_state_get_type(parser, token, type); + + switch (type) { + /* */ + case LXB_CSS_DISPLAY_FLOW: + case LXB_CSS_DISPLAY_FLOW_ROOT: + display->c = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + +done: + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_order(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_integer(parser, token, + &declar->u.order->integer); + if (res) { + declar->u.order->type = LXB_CSS_ORDER__INTEGER; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + declar->u.order->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_visibility(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_VISIBILITY_VISIBLE: + case LXB_CSS_VISIBILITY_HIDDEN: + case LXB_CSS_VISIBILITY_COLLAPSE: + declar->u.visibility->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + case LXB_CSS_VALUE_AUTO: + case LXB_CSS_VALUE_MIN_CONTENT: + case LXB_CSS_VALUE_MAX_CONTENT: + declar->u.width->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + } + + if (!lxb_css_property_state_length_percentage(parser, token, + declar->u.user)) + { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_width(parser, token, ctx); +} + +bool +lxb_css_property_state_box_sizing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + case LXB_CSS_VALUE_CONTENT_BOX: + case LXB_CSS_VALUE_BORDER_BOX: + declar->u.box_sizing->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_min_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_width(parser, token, ctx); +} + +bool +lxb_css_property_state_min_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_width(parser, token, ctx); +} + +bool +lxb_css_property_state_max_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + case LXB_CSS_VALUE_NONE: + case LXB_CSS_VALUE_MIN_CONTENT: + case LXB_CSS_VALUE_MAX_CONTENT: + declar->u.width->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + } + + if (!lxb_css_property_state_length_percentage(parser, token, + declar->u.user)) + { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_max_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_max_width(parser, token, ctx); +} + +static bool +lxb_css_property_state_mp(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_rule_declaration_t *declar, bool with_auto) +{ + unsigned int state; + lxb_css_value_type_t type; + lxb_css_property_margin_top_t *top; + + state = 1; + +next: + + switch (state) { + case 1: + top = &declar->u.margin->top; + break; + + case 2: + top = &declar->u.margin->right; + break; + + case 3: + top = &declar->u.margin->bottom; + break; + + case 4: + top = &declar->u.margin->left; + break; + + default: + return lxb_css_parser_failed(parser); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + top->type = type; + break; + + case LXB_CSS_VALUE_AUTO: + if (with_auto) { + top->type = type; + break; + } + + /* Fall through. */ + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + } + else if (!lxb_css_property_state_length_percentage(parser, token, + (lxb_css_value_length_percentage_t *) top)) + { + return lxb_css_parser_failed(parser); + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + state++; + + goto next; +} + +static bool +lxb_css_property_state_mp_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_rule_declaration_t *declar, bool with_auto) +{ + lxb_css_value_type_t type; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + declar->u.margin_top->type = type; + break; + + case LXB_CSS_VALUE_AUTO: + if (with_auto) { + declar->u.margin_top->type = type; + break; + } + + /* Fall through. */ + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + } + + if (!lxb_css_property_state_length_percentage(parser, token, + declar->u.user)) + { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_margin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp(parser, token, ctx, true); +} + +bool +lxb_css_property_state_margin_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, true); +} + +bool +lxb_css_property_state_margin_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, true); +} + +bool +lxb_css_property_state_margin_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, true); +} + +bool +lxb_css_property_state_margin_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, true); +} + +bool +lxb_css_property_state_padding(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp(parser, token, ctx, false); +} + +bool +lxb_css_property_state_padding_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, false); +} + +bool +lxb_css_property_state_padding_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, false); +} + +bool +lxb_css_property_state_padding_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, false); +} + +bool +lxb_css_property_state_padding_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, false); +} + +static bool +lxb_css_property_state_line_width_style_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_border_t *border) +{ + lxb_status_t status; + lxb_css_value_type_t type; + const lxb_css_data_t *unit; + lxb_css_value_length_t *length; + lxb_css_syntax_token_string_t *str; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + if (border->width.type != LXB_CSS_VALUE__UNDEF) { + return false; + } + + str = &lxb_css_syntax_token_dimension(token)->str; + + unit = lxb_css_unit_absolute_relative_by_name(str->data, + str->length); + if (unit == NULL) { + return false; + } + + length = &border->width.length; + + border->width.type = LXB_CSS_VALUE__LENGTH; + length->num = lxb_css_syntax_token_dimension(token)->num.num; + length->is_float = lxb_css_syntax_token_dimension(token)->num.is_float; + length->unit = (lxb_css_unit_t) unit->unique; + break; + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (border->width.type != LXB_CSS_VALUE__UNDEF) { + return false; + } + + length = &border->width.length; + + border->width.type = LXB_CSS_VALUE__NUMBER; + length->num = lxb_css_syntax_token_number(token)->num; + length->is_float = lxb_css_syntax_token_number(token)->is_float; + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_THIN: + case LXB_CSS_VALUE_MEDIUM: + case LXB_CSS_VALUE_THICK: + if (border->width.type != LXB_CSS_VALUE__UNDEF) { + return false; + } + + border->width.type = type; + break; + + case LXB_CSS_VALUE_NONE: + case LXB_CSS_VALUE_HIDDEN: + case LXB_CSS_VALUE_DOTTED: + case LXB_CSS_VALUE_DASHED: + case LXB_CSS_VALUE_SOLID: + case LXB_CSS_VALUE_DOUBLE: + case LXB_CSS_VALUE_GROOVE: + case LXB_CSS_VALUE_RIDGE: + case LXB_CSS_VALUE_INSET: + case LXB_CSS_VALUE_OUTSET: + if (border->style != LXB_CSS_VALUE__UNDEF) { + return false; + } + + border->style = type; + break; + + default: + goto color; + } + + break; + + default: + goto color; + } + + lxb_css_syntax_parser_consume(parser); + + return true; + +color: + + if (border->color.type != LXB_CSS_VALUE__UNDEF) { + return false; + } + + return lxb_css_property_state_color_handler(parser, token, &border->color, + &status); +} + +bool +lxb_css_property_state_border(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + declar->u.border->style = type; + + lxb_css_syntax_parser_consume(parser); + return lxb_css_parser_success(parser); + + default: + break; + } + } + + res = lxb_css_property_state_line_width_style_color(parser, token, + declar->u.border); + if (!res) { + return lxb_css_parser_failed(parser); + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + res = lxb_css_property_state_line_width_style_color(parser, token, + declar->u.border); + if (!res) { + return lxb_css_parser_failed(parser); + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + res = lxb_css_property_state_line_width_style_color(parser, token, + declar->u.border); + if (!res) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_border_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_border(parser, token, ctx); +} + +bool +lxb_css_property_state_border_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_border(parser, token, ctx); +} + +bool +lxb_css_property_state_border_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_border(parser, token, ctx); +} + +bool +lxb_css_property_state_border_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_border(parser, token, ctx); +} + +bool +lxb_css_property_state_border_top_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_border_right_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_border_bottom_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_border_left_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_background_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_status_t status; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + declar->u.color->type = type; + + lxb_css_syntax_parser_consume(parser); + return lxb_css_parser_success(parser); + + default: + break; + } + } + + res = lxb_css_property_state_color_handler(parser, token, + (lxb_css_value_color_t *) declar->u.color, + &status); + if (!res) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_opacity(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_opacity_t *opacity = declar->u.opacity; + + res = lxb_css_property_state_number_percentage(parser, token, + (lxb_css_value_number_percentage_t *) opacity); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + opacity->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_position(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_POSITION_STATIC: + case LXB_CSS_POSITION_RELATIVE: + case LXB_CSS_POSITION_ABSOLUTE: + case LXB_CSS_POSITION_STICKY: + case LXB_CSS_POSITION_FIXED: + declar->u.position->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_mp_top(parser, token, ctx, true); +} + +bool +lxb_css_property_state_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_inset_block_start(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_inset_inline_start(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_inset_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_inset_inline_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_top(parser, token, ctx); +} + +bool +lxb_css_property_state_text_transform(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_text_transform_t *tt; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + tt = declar->u.text_transform; + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_TRANSFORM_NONE: + tt->type_case = type; + break; + + case LXB_CSS_TEXT_TRANSFORM_CAPITALIZE: + case LXB_CSS_TEXT_TRANSFORM_UPPERCASE: + case LXB_CSS_TEXT_TRANSFORM_LOWERCASE: + tt->type_case = type; + goto next; + + case LXB_CSS_TEXT_TRANSFORM_FULL_WIDTH: + tt->full_width = type; + goto next; + + case LXB_CSS_TEXT_TRANSFORM_FULL_SIZE_KANA: + tt->full_size_kana = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + +next: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + + switch (type) { + case LXB_CSS_TEXT_TRANSFORM_CAPITALIZE: + case LXB_CSS_TEXT_TRANSFORM_UPPERCASE: + case LXB_CSS_TEXT_TRANSFORM_LOWERCASE: + if (tt->type_case != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + tt->type_case = type; + goto next; + + case LXB_CSS_TEXT_TRANSFORM_FULL_WIDTH: + if (tt->full_width != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + tt->full_width = type; + goto next; + + case LXB_CSS_TEXT_TRANSFORM_FULL_SIZE_KANA: + if (tt->full_size_kana != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + tt->full_size_kana = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } +} + +bool +lxb_css_property_state_text_align(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_ALIGN_START: + case LXB_CSS_TEXT_ALIGN_END: + case LXB_CSS_TEXT_ALIGN_LEFT: + case LXB_CSS_TEXT_ALIGN_RIGHT: + case LXB_CSS_TEXT_ALIGN_CENTER: + case LXB_CSS_TEXT_ALIGN_JUSTIFY: + case LXB_CSS_TEXT_ALIGN_MATCH_PARENT: + case LXB_CSS_TEXT_ALIGN_JUSTIFY_ALL: + declar->u.text_align->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_align_all(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_ALIGN_ALL_START: + case LXB_CSS_TEXT_ALIGN_ALL_END: + case LXB_CSS_TEXT_ALIGN_ALL_LEFT: + case LXB_CSS_TEXT_ALIGN_ALL_RIGHT: + case LXB_CSS_TEXT_ALIGN_ALL_CENTER: + case LXB_CSS_TEXT_ALIGN_ALL_JUSTIFY: + case LXB_CSS_TEXT_ALIGN_ALL_MATCH_PARENT: + declar->u.text_align_all->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_align_last(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_ALIGN_LAST_AUTO: + case LXB_CSS_TEXT_ALIGN_LAST_START: + case LXB_CSS_TEXT_ALIGN_LAST_END: + case LXB_CSS_TEXT_ALIGN_LAST_LEFT: + case LXB_CSS_TEXT_ALIGN_LAST_RIGHT: + case LXB_CSS_TEXT_ALIGN_LAST_CENTER: + case LXB_CSS_TEXT_ALIGN_LAST_JUSTIFY: + case LXB_CSS_TEXT_ALIGN_LAST_MATCH_PARENT: + declar->u.text_align_last->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_justify(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_JUSTIFY_AUTO: + case LXB_CSS_TEXT_JUSTIFY_NONE: + case LXB_CSS_TEXT_JUSTIFY_INTER_WORD: + case LXB_CSS_TEXT_JUSTIFY_INTER_CHARACTER: + declar->u.text_justify->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_indent(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_text_indent_t *text_indent; + + text_indent = declar->u.text_indent; + + res = lxb_css_property_state_length_percentage(parser, token, + &text_indent->length); + if (res) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + text_indent->type = LXB_CSS_VALUE__LENGTH; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + if (!res) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + if (res) { + return lxb_css_parser_failed(parser); + } + + text_indent->type = type; + break; + + /* Local. */ + case LXB_CSS_TEXT_INDENT_HANGING: + text_indent->hanging = type; + goto next; + + case LXB_CSS_TEXT_INDENT_EACH_LINE: + text_indent->each_line = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + +next: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_length_percentage(parser, token, + &text_indent->length); + if (res) { + if (text_indent->type != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + text_indent->type = LXB_CSS_VALUE__LENGTH; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + if (text_indent->type == LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + + switch (type) { + case LXB_CSS_TEXT_INDENT_HANGING: + if (text_indent->hanging != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + text_indent->hanging = type; + goto next; + + case LXB_CSS_TEXT_INDENT_EACH_LINE: + if (text_indent->each_line != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + text_indent->each_line = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } +} + +bool +lxb_css_property_state_white_space(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WHITE_SPACE_NORMAL: + case LXB_CSS_WHITE_SPACE_PRE: + case LXB_CSS_WHITE_SPACE_NOWRAP: + case LXB_CSS_WHITE_SPACE_PRE_WRAP: + case LXB_CSS_WHITE_SPACE_BREAK_SPACES: + case LXB_CSS_WHITE_SPACE_PRE_LINE: + declar->u.white_space->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_tab_size(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_number_length(parser, token, + (lxb_css_value_number_length_t *) declar->u.tab_size); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + declar->u.tab_size->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_word_break(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WORD_BREAK_NORMAL: + case LXB_CSS_WORD_BREAK_KEEP_ALL: + case LXB_CSS_WORD_BREAK_BREAK_ALL: + case LXB_CSS_WORD_BREAK_BREAK_WORD: + declar->u.word_break->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_line_break(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_LINE_BREAK_AUTO: + case LXB_CSS_LINE_BREAK_LOOSE: + case LXB_CSS_LINE_BREAK_NORMAL: + case LXB_CSS_LINE_BREAK_STRICT: + case LXB_CSS_LINE_BREAK_ANYWHERE: + declar->u.line_break->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_hyphens(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_HYPHENS_NONE: + case LXB_CSS_HYPHENS_MANUAL: + case LXB_CSS_HYPHENS_AUTO: + declar->u.hyphens->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_overflow_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_OVERFLOW_WRAP_NORMAL: + case LXB_CSS_OVERFLOW_WRAP_BREAK_WORD: + case LXB_CSS_OVERFLOW_WRAP_ANYWHERE: + declar->u.overflow_wrap->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_word_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_overflow_wrap(parser, token, ctx); +} + +bool +lxb_css_property_state_word_spacing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_length(parser, token, + &declar->u.word_spacing->length); + if (res) { + declar->u.word_spacing->type = LXB_CSS_VALUE__LENGTH; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WORD_SPACING_NORMAL: + declar->u.word_spacing->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_letter_spacing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_word_spacing(parser, token, ctx); +} + +bool +lxb_css_property_state_hanging_punctuation(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_hanging_punctuation_t *hp; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + hp = declar->u.hanging_punctuation; + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_HANGING_PUNCTUATION_NONE: + hp->type_first = type; + break; + + case LXB_CSS_HANGING_PUNCTUATION_FIRST: + hp->type_first = type; + goto next; + + case LXB_CSS_HANGING_PUNCTUATION_FORCE_END: + case LXB_CSS_HANGING_PUNCTUATION_ALLOW_END: + hp->force_allow = type; + goto next; + + case LXB_CSS_HANGING_PUNCTUATION_LAST: + hp->last = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + +next: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + + switch (type) { + case LXB_CSS_HANGING_PUNCTUATION_FIRST: + if (hp->type_first != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + hp->type_first = type; + goto next; + + case LXB_CSS_HANGING_PUNCTUATION_FORCE_END: + case LXB_CSS_HANGING_PUNCTUATION_ALLOW_END: + if (hp->force_allow != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + hp->force_allow = type; + goto next; + + case LXB_CSS_HANGING_PUNCTUATION_LAST: + if (hp->last != LXB_CSS_VALUE__UNDEF) { + return lxb_css_parser_failed(parser); + } + + hp->last = type; + goto next; + + default: + return lxb_css_parser_failed(parser); + } +} + +bool +lxb_css_property_state_font_family(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + size_t length; + const lxb_char_t *data; + lexbor_str_t *str; + lexbor_mraw_t *mraw; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_font_family_t *ff; + lxb_css_property_family_name_t *name; + + mraw = parser->memory->mraw; + ff = declar->u.font_family; + + while (token != NULL) { + name = lexbor_mraw_alloc(mraw, sizeof(lxb_css_property_family_name_t)); + if (name == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + data = lxb_css_syntax_token_ident(token)->data; + length = lxb_css_syntax_token_ident(token)->length; + + type = lxb_css_value_by_name(data, length); + if (type != LXB_CSS_VALUE__UNDEF) { + name->generic = true; + name->u.type = type; + + goto next; + } + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_STRING) { + data = lxb_css_syntax_token_string(token)->data; + length = lxb_css_syntax_token_string(token)->length; + } + else { + return lxb_css_parser_failed(parser); + } + + name->generic = false; + + str = &name->u.str; + + (void) lexbor_str_init(str, mraw, length); + if (name->u.str.data == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + memcpy(str->data, data, length); + + str->data[length] = '\0'; + str->length = length; + + next: + + if (ff->first == NULL) { + ff->first = name; + } + else { + ff->last->next = name; + } + + name->next = NULL; + name->prev = ff->last; + ff->last = name; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + } + + lxb_css_property_state_check_token(parser, token); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_font_weight(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx)\ +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_font_weight_t *fw = declar->u.font_weight; + + res = lxb_css_property_state_number(parser, token, &fw->number); + + if (res) { + if (fw->number.num < 1 || fw->number.num > 1000) { + return lxb_css_parser_failed(parser); + } + + fw->type = LXB_CSS_FONT_WEIGHT__NUMBER; + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FONT_WEIGHT_NORMAL: + case LXB_CSS_FONT_WEIGHT_BOLD: + case LXB_CSS_FONT_WEIGHT_BOLDER: + case LXB_CSS_FONT_WEIGHT_LIGHTER: + fw->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_font_stretch(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_font_stretch_t *fs = declar->u.font_stretch; + + res = lxb_css_property_state_percentage(parser, token, &fs->percentage); + + if (res) { + if (fs->percentage.num < 0) { + return lxb_css_parser_failed(parser); + } + + fs->type = LXB_CSS_FONT_STRETCH__PERCENTAGE; + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FONT_STRETCH_NORMAL: + case LXB_CSS_FONT_STRETCH_ULTRA_CONDENSED: + case LXB_CSS_FONT_STRETCH_EXTRA_CONDENSED: + case LXB_CSS_FONT_STRETCH_CONDENSED: + case LXB_CSS_FONT_STRETCH_SEMI_CONDENSED: + case LXB_CSS_FONT_STRETCH_SEMI_EXPANDED: + case LXB_CSS_FONT_STRETCH_EXPANDED: + case LXB_CSS_FONT_STRETCH_EXTRA_EXPANDED: + case LXB_CSS_FONT_STRETCH_ULTRA_EXPANDED: + fs->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_font_style(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_font_style_t *fs = declar->u.font_style; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FONT_STYLE_NORMAL: + case LXB_CSS_FONT_STYLE_ITALIC: + fs->type = type; + break; + + case LXB_CSS_FONT_STYLE_OBLIQUE: + fs->type = type; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_angle(parser, token, &fs->angle); + + if (res) { + if (fs->angle.num < -90 || fs->angle.num > 90) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); + } + else { + fs->angle.unit = (lxb_css_unit_angle_t) LXB_CSS_UNIT__UNDEF; + } + + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_font_size(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_font_size_t *fs = declar->u.font_size; + + res = lxb_css_property_state_length_percentage(parser, token, &fs->length); + + if (res) { + if (fs->length.u.length.num < 0) { + return lxb_css_parser_failed(parser); + } + + fs->type = LXB_CSS_FONT_SIZE__LENGTH; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FONT_SIZE_XX_SMALL: + case LXB_CSS_FONT_SIZE_X_SMALL: + case LXB_CSS_FONT_SIZE_SMALL: + case LXB_CSS_FONT_SIZE_MEDIUM: + case LXB_CSS_FONT_SIZE_LARGE: + case LXB_CSS_FONT_SIZE_X_LARGE: + case LXB_CSS_FONT_SIZE_XX_LARGE: + case LXB_CSS_FONT_SIZE_XXX_LARGE: + case LXB_CSS_FONT_SIZE_MATH: + case LXB_CSS_FONT_SIZE_LARGER: + case LXB_CSS_FONT_SIZE_SMALLER: + fs->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_float_reference(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLOAT_REFERENCE_INLINE: + case LXB_CSS_FLOAT_REFERENCE_COLUMN: + case LXB_CSS_FLOAT_REFERENCE_REGION: + case LXB_CSS_FLOAT_REFERENCE_PAGE: + declar->u.float_reference->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_float(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_float_t *fp = declar->u.floatp; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + if (token->type == LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + goto snap; + } + + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLOAT_BLOCK_START: + case LXB_CSS_FLOAT_BLOCK_END: + case LXB_CSS_FLOAT_INLINE_START: + case LXB_CSS_FLOAT_INLINE_END: + case LXB_CSS_FLOAT_SNAP_BLOCK: + case LXB_CSS_FLOAT_SNAP_INLINE: + case LXB_CSS_FLOAT_LEFT: + case LXB_CSS_FLOAT_RIGHT: + case LXB_CSS_FLOAT_TOP: + case LXB_CSS_FLOAT_BOTTOM: + case LXB_CSS_FLOAT_NONE: + fp->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + +snap: + + type = lxb_css_value_by_name(lxb_css_syntax_token_function(token)->data, + lxb_css_syntax_token_function(token)->length); + + if (type != LXB_CSS_FLOAT_SNAP_BLOCK + && type != LXB_CSS_FLOAT_SNAP_INLINE) + { + return lxb_css_parser_failed(parser); + } + + fp->type = type; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_length(parser, token, &fp->length.length); + if (!res) { + return lxb_css_parser_failed(parser); + } + + fp->length.type = LXB_CSS_VALUE__LENGTH; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + fp->snap_type = LXB_CSS_VALUE__UNDEF; + + lxb_css_syntax_parser_consume(parser); + return lxb_css_parser_success(parser); + } + + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_FLOAT_START: + case LXB_CSS_FLOAT_END: + if (fp->type != LXB_CSS_FLOAT_SNAP_BLOCK) { + return lxb_css_parser_failed(parser); + } + + fp->snap_type = type; + break; + + case LXB_CSS_FLOAT_LEFT: + case LXB_CSS_FLOAT_RIGHT: + if (fp->type != LXB_CSS_FLOAT_SNAP_INLINE) { + return lxb_css_parser_failed(parser); + } + + fp->snap_type = type; + break; + + case LXB_CSS_FLOAT_NEAR: + fp->snap_type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) { + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + } + + return lxb_css_parser_failed(parser); +} + +bool +lxb_css_property_state_clear(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_CLEAR_INLINE_START: + case LXB_CSS_CLEAR_INLINE_END: + case LXB_CSS_CLEAR_BLOCK_START: + case LXB_CSS_CLEAR_BLOCK_END: + case LXB_CSS_CLEAR_LEFT: + case LXB_CSS_CLEAR_RIGHT: + case LXB_CSS_CLEAR_TOP: + case LXB_CSS_CLEAR_BOTTOM: + case LXB_CSS_CLEAR_NONE: + declar->u.clear->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_float_defer(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_float_defer_t *fd = declar->u.float_defer; + + res = lxb_css_property_state_integer(parser, token, &fd->integer); + if (res) { + fd->type = LXB_CSS_FLOAT_DEFER__INTEGER; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLOAT_DEFER_LAST: + case LXB_CSS_FLOAT_DEFER_NONE: + fd->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_float_offset(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_float_offset_t *fo = declar->u.float_offset; + + res = lxb_css_property_state_length_percentage(parser, token, + (lxb_css_value_length_percentage_t *) fo); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + fo->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_wrap_flow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WRAP_FLOW_AUTO: + case LXB_CSS_WRAP_FLOW_BOTH: + case LXB_CSS_WRAP_FLOW_START: + case LXB_CSS_WRAP_FLOW_END: + case LXB_CSS_WRAP_FLOW_MINIMUM: + case LXB_CSS_WRAP_FLOW_MAXIMUM: + case LXB_CSS_WRAP_FLOW_CLEAR: + declar->u.wrap_flow->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_wrap_through(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WRAP_THROUGH_WRAP: + case LXB_CSS_WRAP_THROUGH_NONE: + declar->u.wrap_through->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_direction(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLEX_DIRECTION_ROW: + case LXB_CSS_FLEX_DIRECTION_ROW_REVERSE: + case LXB_CSS_FLEX_DIRECTION_COLUMN: + case LXB_CSS_FLEX_DIRECTION_COLUMN_REVERSE: + declar->u.flex_direction->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLEX_WRAP_NOWRAP: + case LXB_CSS_FLEX_WRAP_WRAP: + case LXB_CSS_FLEX_WRAP_WRAP_REVERSE: + declar->u.flex_wrap->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_flow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_flex_flow_t *ff = declar->u.flex_flow; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLEX_DIRECTION_ROW: + case LXB_CSS_FLEX_DIRECTION_ROW_REVERSE: + case LXB_CSS_FLEX_DIRECTION_COLUMN: + case LXB_CSS_FLEX_DIRECTION_COLUMN_REVERSE: + ff->type_direction = type; + goto direction; + + case LXB_CSS_FLEX_WRAP_NOWRAP: + case LXB_CSS_FLEX_WRAP_WRAP: + case LXB_CSS_FLEX_WRAP_WRAP_REVERSE: + ff->wrap = type; + goto wrap; + + default: + return lxb_css_parser_failed(parser); + } + +direction: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_FLEX_WRAP_NOWRAP: + case LXB_CSS_FLEX_WRAP_WRAP: + case LXB_CSS_FLEX_WRAP_WRAP_REVERSE: + ff->wrap = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + goto done; + +wrap: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_success(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_FLEX_DIRECTION_ROW: + case LXB_CSS_FLEX_DIRECTION_ROW_REVERSE: + case LXB_CSS_FLEX_DIRECTION_COLUMN: + case LXB_CSS_FLEX_DIRECTION_COLUMN_REVERSE: + ff->type_direction = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + +done: + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +lxb_inline bool +lxb_css_property_state_flex_grow_shrink(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_flex_t *flex) +{ + bool res; + + res = lxb_css_property_state_number(parser, token, &flex->grow.number); + if (!res) { + return false; + } + + flex->grow.type = LXB_CSS_FLEX_GROW__NUMBER; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_number(parser, token, &flex->shrink.number); + if (res) { + flex->shrink.type = LXB_CSS_FLEX_SHRINK__NUMBER; + } + + return true; +} + +lxb_inline bool +lxb_css_property_state_flex_grow_basis(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_flex_t *flex) +{ + bool res; + lxb_css_value_type_t type; + + res = lxb_css_property_state_width_handler(parser, token, + (lxb_css_property_flex_basis_t *) &flex->basis); + if (res) { + return true; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + + if (type == LXB_CSS_FLEX_BASIS_CONTENT) { + flex->basis.type = type; + + lxb_css_syntax_parser_consume(parser); + return true; + } + + return false; +} + +bool +lxb_css_property_state_flex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_flex_t *flex = declar->u.flex; + + res = lxb_css_property_state_flex_grow_shrink(parser, token, flex); + + if (res) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_flex_grow_basis(parser, token, flex); + + if (!res && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + flex->basis.type = LXB_CSS_VALUE__NUMBER; + flex->basis.u.length.num = flex->grow.number.num; + flex->basis.u.length.unit = LXB_CSS_UNIT__UNDEF; + flex->basis.u.length.is_float = flex->grow.number.is_float; + + flex->grow.type = LXB_CSS_VALUE__UNDEF; + + if (flex->shrink.type != LXB_CSS_VALUE__UNDEF) { + flex->grow = flex->shrink; + flex->shrink.type = LXB_CSS_VALUE__UNDEF; + + goto try_shrink_last; + } + + res = lxb_css_property_state_flex_grow_shrink(parser, token, flex); + if (!res) { + return lxb_css_parser_failed(parser); + } + } + + return lxb_css_parser_success(parser); + } + else { + res = lxb_css_property_state_flex_grow_basis(parser, token, flex); + + if (res) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + (void) lxb_css_property_state_flex_grow_shrink(parser, token, flex); + + return lxb_css_parser_success(parser); + } + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_FLEX_NONE: + flex->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); + +try_shrink_last: + + res = lxb_css_property_state_number(parser, token, &flex->shrink.number); + if (res) { + flex->shrink.type = LXB_CSS_FLEX_SHRINK__NUMBER; + } + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_grow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_flex_grow_t *fg = declar->u.flex_grow; + + res = lxb_css_property_state_number(parser, token, &fg->number); + if (res) { + if (fg->number.num < 0) { + return lxb_css_parser_failed(parser); + } + + fg->type = LXB_CSS_FLEX_GROW__NUMBER; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + fg->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_shrink(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_flex_grow_t *fs = declar->u.flex_shrink; + + res = lxb_css_property_state_number(parser, token, &fs->number); + if (res) { + if (fs->number.num < 0) { + return lxb_css_parser_failed(parser); + } + + fs->type = LXB_CSS_FLEX_SHRINK__NUMBER; + + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + fs->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_flex_basis(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_flex_basis_t *fb = declar->u.flex_basis; + + res = lxb_css_property_state_width_handler(parser, token, + (lxb_css_property_width_t *) fb); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + case LXB_CSS_FLEX_BASIS_CONTENT: + fb->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_justify_content(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_JUSTIFY_CONTENT_FLEX_START: + case LXB_CSS_JUSTIFY_CONTENT_FLEX_END: + case LXB_CSS_JUSTIFY_CONTENT_CENTER: + case LXB_CSS_JUSTIFY_CONTENT_SPACE_BETWEEN: + case LXB_CSS_JUSTIFY_CONTENT_SPACE_AROUND: + declar->u.justify_content->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_align_items(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_ALIGN_ITEMS_FLEX_START: + case LXB_CSS_ALIGN_ITEMS_FLEX_END: + case LXB_CSS_ALIGN_ITEMS_CENTER: + case LXB_CSS_ALIGN_ITEMS_BASELINE: + case LXB_CSS_ALIGN_ITEMS_STRETCH: + declar->u.align_items->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_align_self(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_ALIGN_SELF_AUTO: + case LXB_CSS_ALIGN_SELF_FLEX_START: + case LXB_CSS_ALIGN_SELF_FLEX_END: + case LXB_CSS_ALIGN_SELF_CENTER: + case LXB_CSS_ALIGN_SELF_BASELINE: + case LXB_CSS_ALIGN_SELF_STRETCH: + declar->u.align_self->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_align_content(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_ALIGN_CONTENT_FLEX_START: + case LXB_CSS_ALIGN_CONTENT_FLEX_END: + case LXB_CSS_ALIGN_CONTENT_CENTER: + case LXB_CSS_ALIGN_CONTENT_SPACE_BETWEEN: + case LXB_CSS_ALIGN_CONTENT_SPACE_AROUND: + case LXB_CSS_ALIGN_CONTENT_STRETCH: + declar->u.align_content->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_dominant_baseline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_DOMINANT_BASELINE_AUTO: + case LXB_CSS_DOMINANT_BASELINE_TEXT_BOTTOM: + case LXB_CSS_DOMINANT_BASELINE_ALPHABETIC: + case LXB_CSS_DOMINANT_BASELINE_IDEOGRAPHIC: + case LXB_CSS_DOMINANT_BASELINE_MIDDLE: + case LXB_CSS_DOMINANT_BASELINE_CENTRAL: + case LXB_CSS_DOMINANT_BASELINE_MATHEMATICAL: + case LXB_CSS_DOMINANT_BASELINE_HANGING: + case LXB_CSS_DOMINANT_BASELINE_TEXT_TOP: + declar->u.dominant_baseline->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_alignment_baseline_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_alignment_baseline_t *ab) +{ + lxb_css_value_type_t type; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_ALIGNMENT_BASELINE_BASELINE: + case LXB_CSS_ALIGNMENT_BASELINE_TEXT_BOTTOM: + case LXB_CSS_ALIGNMENT_BASELINE_ALPHABETIC: + case LXB_CSS_ALIGNMENT_BASELINE_IDEOGRAPHIC: + case LXB_CSS_ALIGNMENT_BASELINE_MIDDLE: + case LXB_CSS_ALIGNMENT_BASELINE_CENTRAL: + case LXB_CSS_ALIGNMENT_BASELINE_MATHEMATICAL: + case LXB_CSS_ALIGNMENT_BASELINE_TEXT_TOP: + ab->type = type; + + lxb_css_syntax_parser_consume(parser); + return true; + + default: + return false; + } +} + +bool +lxb_css_property_state_baseline_shift_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_baseline_shift_t *bs) +{ + bool res; + lxb_css_value_type_t type; + + res = lxb_css_property_state_length_percentage(parser, token, bs); + + if (res) { + return true; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_BASELINE_SHIFT_SUB: + case LXB_CSS_BASELINE_SHIFT_SUPER: + case LXB_CSS_BASELINE_SHIFT_TOP: + case LXB_CSS_BASELINE_SHIFT_CENTER: + case LXB_CSS_BASELINE_SHIFT_BOTTOM: + bs->type = type; + + lxb_css_syntax_parser_consume(parser); + return true; + + default: + return false; + } +} + +bool +lxb_css_property_state_vertical_align(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + uint8_t maps; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_vertical_align_t *va = declar->u.vertical_align; + + maps = 0; + +again: + + res = lxb_css_property_state_alignment_baseline_h(parser, token, + &va->alignment); + if (res) { + if (maps & 1 << 1) { + return lxb_css_parser_failed(parser); + } + + maps |= 1 << 1; + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_baseline_shift_h(parser, token, + &va->shift); + if (res) { + if (maps & 1 << 2) { + return lxb_css_parser_failed(parser); + } + + maps |= 1 << 2; + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + } + else { + res = lxb_css_property_state_baseline_shift_h(parser, token, + &va->shift); + if (res) { + if (maps & 1 << 2) { + return lxb_css_parser_failed(parser); + } + + maps |= 1 << 2; + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_alignment_baseline_h(parser, token, + &va->alignment); + if (res) { + if (maps & 1 << 1) { + return lxb_css_parser_failed(parser); + } + + maps |= 1 << 1; + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + } + } + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + if (maps != 0) { + return lxb_css_parser_success(parser); + } + + return lxb_css_parser_failed(parser); + } + + if (maps & 1 << 3) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_VERTICAL_ALIGN_FIRST: + case LXB_CSS_VERTICAL_ALIGN_LAST: + va->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + maps = 1 << 3; + + goto again; +} + +bool +lxb_css_property_state_baseline_source(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_BASELINE_SOURCE_AUTO: + case LXB_CSS_BASELINE_SOURCE_FIRST: + case LXB_CSS_BASELINE_SOURCE_LAST: + declar->u.baseline_source->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_alignment_baseline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_ALIGNMENT_BASELINE_BASELINE: + case LXB_CSS_ALIGNMENT_BASELINE_TEXT_BOTTOM: + case LXB_CSS_ALIGNMENT_BASELINE_ALPHABETIC: + case LXB_CSS_ALIGNMENT_BASELINE_IDEOGRAPHIC: + case LXB_CSS_ALIGNMENT_BASELINE_MIDDLE: + case LXB_CSS_ALIGNMENT_BASELINE_CENTRAL: + case LXB_CSS_ALIGNMENT_BASELINE_MATHEMATICAL: + case LXB_CSS_ALIGNMENT_BASELINE_TEXT_TOP: + declar->u.alignment_baseline->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_baseline_shift(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_length_percentage(parser, token, + declar->u.baseline_shift); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_BASELINE_SHIFT_SUB: + case LXB_CSS_BASELINE_SHIFT_SUPER: + case LXB_CSS_BASELINE_SHIFT_TOP: + case LXB_CSS_BASELINE_SHIFT_CENTER: + case LXB_CSS_BASELINE_SHIFT_BOTTOM: + declar->u.baseline_shift->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_line_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_number_length_percentage(parser, token, + declar->u.line_height); + if (res) { + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_LINE_HEIGHT_NORMAL: + declar->u.line_height->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_z_index(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + res = lxb_css_property_state_integer(parser, token, + &declar->u.z_index->integer); + if (res) { + declar->u.z_index->type = LXB_CSS_VALUE__INTEGER; + return lxb_css_parser_success(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_Z_INDEX_AUTO: + declar->u.line_height->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_direction(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_DIRECTION_LTR: + case LXB_CSS_DIRECTION_RTL: + declar->u.direction->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_unicode_bidi(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_UNICODE_BIDI_NORMAL: + case LXB_CSS_UNICODE_BIDI_EMBED: + case LXB_CSS_UNICODE_BIDI_ISOLATE: + case LXB_CSS_UNICODE_BIDI_BIDI_OVERRIDE: + case LXB_CSS_UNICODE_BIDI_ISOLATE_OVERRIDE: + case LXB_CSS_UNICODE_BIDI_PLAINTEXT: + declar->u.unicode_bidi->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_writing_mode(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_WRITING_MODE_HORIZONTAL_TB: + case LXB_CSS_WRITING_MODE_VERTICAL_RL: + case LXB_CSS_WRITING_MODE_VERTICAL_LR: + case LXB_CSS_WRITING_MODE_SIDEWAYS_RL: + case LXB_CSS_WRITING_MODE_SIDEWAYS_LR: + declar->u.writing_mode->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_orientation(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_ORIENTATION_MIXED: + case LXB_CSS_TEXT_ORIENTATION_UPRIGHT: + case LXB_CSS_TEXT_ORIENTATION_SIDEWAYS: + declar->u.text_orientation->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_combine_upright(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_text_combine_upright_t *tcu = declar->u.text_combine_upright; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_COMBINE_UPRIGHT_NONE: + case LXB_CSS_TEXT_COMBINE_UPRIGHT_ALL: + tcu->type = type; + break; + + case LXB_CSS_TEXT_COMBINE_UPRIGHT_DIGITS: + tcu->type = type; + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + res = lxb_css_property_state_integer(parser, token, + &tcu->digits); + if (res) { + if (tcu->digits.num != 2 && tcu->digits.num != 4) { + return lxb_css_parser_failed(parser); + } + } + + return lxb_css_parser_success(parser); + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_overflow_x(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_OVERFLOW_X_VISIBLE: + case LXB_CSS_OVERFLOW_X_HIDDEN: + case LXB_CSS_OVERFLOW_X_CLIP: + case LXB_CSS_OVERFLOW_X_SCROLL: + case LXB_CSS_OVERFLOW_X_AUTO: + declar->u.overflow_x->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_overflow_y(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_overflow_x(parser, token, ctx); +} + +bool +lxb_css_property_state_overflow_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_overflow_x(parser, token, ctx); +} + +bool +lxb_css_property_state_overflow_inline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_overflow_x(parser, token, ctx); +} + +bool +lxb_css_property_state_text_overflow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_OVERFLOW_CLIP: + case LXB_CSS_TEXT_OVERFLOW_ELLIPSIS: + declar->u.text_overflow->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +lxb_status_t +lxb_css_property_state_text_decoration_line_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_text_decoration_line_t *tdl) +{ + lxb_css_value_type_t type; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return LXB_STATUS_NEXT; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_TEXT_DECORATION_LINE_NONE: + tdl->type = type; + + lxb_css_syntax_parser_consume(parser); + return LXB_STATUS_OK; + + default: + goto first; + } + +next: + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token_wo_ws(parser); + if (token == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return LXB_STATUS_OK; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + +first: + + switch (type) { + case LXB_CSS_TEXT_DECORATION_LINE_UNDERLINE: + if (tdl->underline != LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_STOP; + } + + tdl->underline = type; + goto next; + + case LXB_CSS_TEXT_DECORATION_LINE_OVERLINE: + if (tdl->overline != LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_STOP; + } + + tdl->overline = type; + goto next; + + case LXB_CSS_TEXT_DECORATION_LINE_LINE_THROUGH: + if (tdl->line_through != LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_STOP; + } + + tdl->line_through = type; + goto next; + + case LXB_CSS_TEXT_DECORATION_LINE_BLINK: + if (tdl->blink != LXB_CSS_VALUE__UNDEF) { + return LXB_STATUS_STOP; + } + + tdl->blink = type; + goto next; + + default: + if (tdl->underline != LXB_CSS_VALUE__UNDEF + || tdl->overline != LXB_CSS_VALUE__UNDEF + || tdl->line_through != LXB_CSS_VALUE__UNDEF + || tdl->blink != LXB_CSS_VALUE__UNDEF) + { + return LXB_STATUS_OK; + } + + return LXB_STATUS_NEXT; + } +} + +bool +lxb_css_property_state_text_decoration_line(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_status_t status; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_text_decoration_line_t *tdl = declar->u.text_decoration_line; + + status = lxb_css_property_state_text_decoration_line_h(parser, token, tdl); + + if (status == LXB_STATUS_OK) { + return lxb_css_parser_success(parser); + } + else if (status == LXB_STATUS_STOP) { + return lxb_css_parser_failed(parser); + } + else if (status != LXB_STATUS_NEXT) { + return lxb_css_parser_memory_fail(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + tdl->type = type; + + lxb_css_syntax_parser_consume(parser); + return lxb_css_parser_success(parser); + + default: + return lxb_css_parser_failed(parser); + } +} + +bool +lxb_css_property_state_text_decoration_style_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_property_text_decoration_style_t *tds) +{ + lxb_css_value_type_t type; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + case LXB_CSS_TEXT_DECORATION_STYLE_SOLID: + case LXB_CSS_TEXT_DECORATION_STYLE_DOUBLE: + case LXB_CSS_TEXT_DECORATION_STYLE_DOTTED: + case LXB_CSS_TEXT_DECORATION_STYLE_DASHED: + case LXB_CSS_TEXT_DECORATION_STYLE_WAVY: + tds->type = type; + + lxb_css_syntax_parser_consume(parser); + return true; + + default: + return false; + } +} + +bool +lxb_css_property_state_text_decoration_style(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return lxb_css_parser_failed(parser); + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + /* Local. */ + case LXB_CSS_TEXT_DECORATION_STYLE_SOLID: + case LXB_CSS_TEXT_DECORATION_STYLE_DOUBLE: + case LXB_CSS_TEXT_DECORATION_STYLE_DOTTED: + case LXB_CSS_TEXT_DECORATION_STYLE_DASHED: + case LXB_CSS_TEXT_DECORATION_STYLE_WAVY: + declar->u.text_decoration_style->type = type; + break; + + default: + return lxb_css_parser_failed(parser); + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_parser_success(parser); +} + +bool +lxb_css_property_state_text_decoration_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_property_state_color(parser, token, ctx); +} + +bool +lxb_css_property_state_text_decoration(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + bool res, line, style, color; + lxb_status_t status; + lxb_css_value_type_t type; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_property_text_decoration_t *td = declar->u.text_decoration; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + goto lsc; + } + + type = lxb_css_value_by_name(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length); + switch (type) { + /* Global. */ + case LXB_CSS_VALUE_INITIAL: + case LXB_CSS_VALUE_INHERIT: + case LXB_CSS_VALUE_UNSET: + case LXB_CSS_VALUE_REVERT: + td->line.type = type; + + lxb_css_syntax_parser_consume(parser); + return lxb_css_parser_success(parser); + + default: + break; + } + +lsc: + + line = false; + style = false; + color = false; + + for (size_t i = 0; i < 3; i++) { + if (!line) { + status = lxb_css_property_state_text_decoration_line_h(parser, token, + &td->line); + if (status == LXB_STATUS_OK) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + line = true; + } + else if (status == LXB_STATUS_STOP) { + return lxb_css_parser_failed(parser); + } + else if (status != LXB_STATUS_NEXT) { + return lxb_css_parser_memory_fail(parser); + } + } + + if (!style) { + res = lxb_css_property_state_text_decoration_style_h(parser, token, + &td->style); + if (res) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + style = true; + } + } + + if (!color) { + res = lxb_css_property_state_color_handler(parser, token, + (lxb_css_value_color_t *) &td->color, + &status); + if (res) { + token = lxb_css_syntax_parser_token_wo_ws(parser); + lxb_css_property_state_check_token(parser, token); + + color = true; + } + else { + if (status != LXB_STATUS_OK) { + return lxb_css_parser_failed(parser); + } + } + } + } + + if (!line && !style && !color) { + return lxb_css_parser_failed(parser); + } + + return lxb_css_parser_success(parser); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.h new file mode 100644 index 0000000..7d60f4f --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/state.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2021-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_PROPERTY_STATE_H +#define LXB_CSS_PROPERTY_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" + + +LXB_API bool +lxb_css_property_state__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state__custom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_display(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_order(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_visibility(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_box_sizing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_min_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_min_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_max_width(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_max_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_margin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_margin_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_margin_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_margin_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_margin_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_padding(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_padding_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_padding_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_padding_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_padding_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_top_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_right_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_bottom_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_border_left_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_background_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_opacity(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_position(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_top(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_right(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_bottom(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_left(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_inset_block_start(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_inset_inline_start(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_inset_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_inset_inline_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_transform(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_align(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_align_all(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_align_last(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_justify(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_indent(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_white_space(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_tab_size(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_word_break(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_line_break(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_hyphens(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_overflow_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_word_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_word_spacing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_letter_spacing(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_hanging_punctuation(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_font_family(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_font_weight(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_font_stretch(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_font_style(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_font_size(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_float_reference(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_float(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_clear(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_float_defer(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_float_offset(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_wrap_flow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_wrap_through(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_direction(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_wrap(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_flow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_grow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_shrink(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_flex_basis(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_justify_content(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_align_items(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_align_self(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_align_content(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_z_index(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_direction(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_unicode_bidi(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_writing_mode(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_orientation(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_combine_upright(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_overflow_x(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_overflow_y(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_overflow_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_overflow_inline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_overflow(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_decoration_line(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_decoration_style(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_decoration_color(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_text_decoration(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_dominant_baseline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_vertical_align(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_baseline_source(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_alignment_baseline(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_baseline_shift(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +LXB_API bool +lxb_css_property_state_line_height(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_PROPERTY_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/types.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/types.h new file mode 100644 index 0000000..3a23f12 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/property/types.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_PROPERTY_TYPES_H +#define LXB_CSS_PROPERTY_TYPES_H + + + + + +#endif /* LXB_CSS_PROPERTY_TYPES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.c new file mode 100644 index 0000000..5ee9387 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.c @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/rule.h" +#include "lexbor/css/parser.h" +#include "lexbor/core/serialize.h" +#include "lexbor/css/stylesheet.h" +#include "lexbor/css/selectors/selectors.h" + + +void * +lxb_css_rule_destroy(lxb_css_rule_t *rule, bool self_destroy) +{ + switch (rule->type) { + case LXB_CSS_RULE_LIST: + return lxb_css_rule_list_destroy(lxb_css_rule_list(rule), + self_destroy); + case LXB_CSS_RULE_AT_RULE: + return lxb_css_rule_at_destroy(lxb_css_rule_at(rule), + self_destroy); + case LXB_CSS_RULE_STYLE: + return lxb_css_rule_style_destroy(lxb_css_rule_style(rule), + self_destroy); + case LXB_CSS_RULE_BAD_STYLE: + return lxb_css_rule_bad_style_destroy(lxb_css_rule_bad_style(rule), + self_destroy); + case LXB_CSS_RULE_DECLARATION: + return lxb_css_rule_declaration_destroy(lxb_css_rule_declaration(rule), + self_destroy); + case LXB_CSS_RULE_DECLARATION_LIST: + return lxb_css_rule_declaration_list_destroy(lxb_css_rule_declaration_list(rule), + self_destroy); + case LXB_CSS_RULE_STYLESHEET: + case LXB_CSS_RULE_UNDEF: + return NULL; + } + + return NULL; +} + +lxb_status_t +lxb_css_rule_serialize(const lxb_css_rule_t *rule, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (rule->type) { + case LXB_CSS_RULE_LIST: + return lxb_css_rule_list_serialize(lxb_css_rule_list(rule), cb, ctx); + + case LXB_CSS_RULE_AT_RULE: + return lxb_css_rule_at_serialize(lxb_css_rule_at(rule), cb, ctx); + + case LXB_CSS_RULE_STYLE: + return lxb_css_rule_style_serialize(lxb_css_rule_style(rule), + cb, ctx); + case LXB_CSS_RULE_BAD_STYLE: + return lxb_css_rule_bad_style_serialize(lxb_css_rule_bad_style(rule), + cb, ctx); + case LXB_CSS_RULE_DECLARATION: + return lxb_css_rule_declaration_serialize(lxb_css_rule_declaration(rule), + cb, ctx); + case LXB_CSS_RULE_DECLARATION_LIST: + return lxb_css_rule_declaration_list_serialize(lxb_css_rule_declaration_list(rule), + cb, ctx); + case LXB_CSS_RULE_STYLESHEET: + case LXB_CSS_RULE_UNDEF: + break; + } + + return LXB_STATUS_ERROR_WRONG_ARGS; +} + +lxb_status_t +lxb_css_rule_serialize_chain(const lxb_css_rule_t *rule, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + static const lxb_char_t ws_str[] = "\n"; + + status = lxb_css_rule_serialize(rule, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + rule = rule->next; + + while (rule != NULL) { + lexbor_serialize_write(cb, ws_str, (sizeof(ws_str) - 1), ctx, status); + + status = lxb_css_rule_serialize(rule, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + rule = rule->next; + } + + return LXB_STATUS_OK; +} + +lxb_css_rule_list_t * +lxb_css_rule_list_destroy(lxb_css_rule_list_t *list, bool self_destroy) +{ + lxb_css_rule_t *rule, *next, *parent; + lxb_css_rule_t *child_rule; + lxb_css_memory_t *memory; + lxb_css_rule_at_t *at; + lxb_css_rule_style_t *style; + lxb_css_rule_bad_style_t *bad; + lxb_css_rule_list_t *sub_list; + lxb_css_rule_declaration_list_t *declr_list; + + if (list == NULL) { + return NULL; + } + + rule = list->first; + memory = lxb_css_rule(list)->memory; + + while (rule != NULL) { + child_rule = NULL; + + switch (rule->type) { + case LXB_CSS_RULE_LIST: + sub_list = lxb_css_rule_list(rule); + + if (sub_list->first != NULL) { + child_rule = sub_list->first; + + sub_list->first = NULL; + sub_list->last = NULL; + } + break; + + case LXB_CSS_RULE_STYLE: + style = lxb_css_rule_style(rule); + + if (style->child != NULL) { + child_rule = lxb_css_rule(style->child); + style->child = NULL; + } + break; + + case LXB_CSS_RULE_BAD_STYLE: + bad = lxb_css_rule_bad_style(rule); + + if (bad->child != NULL) { + child_rule = lxb_css_rule(bad->child); + bad->child = NULL; + } + break; + + case LXB_CSS_RULE_AT_RULE: + at = lxb_css_rule_at(rule); + + if (at->type == LXB_CSS_AT_RULE_MEDIA) { + if (at->u.media != NULL && at->u.media->block != NULL) { + child_rule = lxb_css_rule(at->u.media->block); + at->u.media->block = NULL; + } + } + else if (at->type == LXB_CSS_AT_RULE__UNDEF) { + if (at->u.undef != NULL && at->u.undef->block != NULL) { + child_rule = lxb_css_rule(at->u.undef->block); + at->u.undef->block = NULL; + } + } + else if (at->type == LXB_CSS_AT_RULE__CUSTOM) { + if (at->u.custom != NULL && at->u.custom->block != NULL) { + child_rule = lxb_css_rule(at->u.custom->block); + at->u.custom->block = NULL; + } + } + else if (at->type == LXB_CSS_AT_RULE_FONT_FACE) { + if (at->u.font_face != NULL && at->u.font_face->block != NULL) { + child_rule = lxb_css_rule(at->u.font_face->block); + at->u.font_face->block = NULL; + } + } + break; + + case LXB_CSS_RULE_DECLARATION_LIST: + declr_list = lxb_css_rule_declaration_list(rule); + + if (declr_list->first != NULL) { + child_rule = declr_list->first; + + declr_list->first = NULL; + declr_list->last = NULL; + } + break; + + default: + break; + } + + if (child_rule != NULL) { + rule = child_rule; + continue; + } + + next = rule->next; + parent = rule->parent; + + (void) lxb_css_rule_destroy(rule, true); + + if (next != NULL) { + rule = next; + } + else { + if (parent == lxb_css_rule(list)) { + break; + } + + rule = parent; + } + } + + if (self_destroy) { + return lexbor_mraw_free(memory->tree, list); + } + + list->first = NULL; + list->last = NULL; + + return list; +} + +lxb_status_t +lxb_css_rule_list_serialize(const lxb_css_rule_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + lxb_css_rule_t *rule; + + static const lxb_char_t nl_str[] = "\n"; + + rule = list->first; + + if (rule == NULL) { + return LXB_STATUS_OK; + } + + status = lxb_css_rule_serialize(rule, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + rule = rule->next; + + while (rule != NULL) { + lexbor_serialize_write(cb, nl_str, (sizeof(nl_str) - 1), ctx, status); + + status = lxb_css_rule_serialize(rule, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + rule = rule->next; + } + + return LXB_STATUS_OK; +} + +lxb_css_rule_at_t * +lxb_css_rule_at_destroy(lxb_css_rule_at_t *at, bool self_destroy) +{ + lxb_css_memory_t *memory = lxb_css_rule(at)->memory; + + switch (at->type) { + case LXB_CSS_AT_RULE__UNDEF: + (void) lxb_css_at_rule__undef_destroy(memory, at->u.undef, true); + break; + + case LXB_CSS_AT_RULE_MEDIA: + (void) lxb_css_at_rule_media_destroy(memory, at->u.media, true); + break; + + case LXB_CSS_AT_RULE_NAMESPACE: + (void) lxb_css_at_rule_namespace_destroy(memory, at->u.ns, true); + break; + + case LXB_CSS_AT_RULE__LAST_ENTRY: + break; + } + + if (self_destroy) { + return lexbor_mraw_free(memory->tree, at); + } + + return at; +} + +lxb_status_t +lxb_css_rule_at_serialize(const lxb_css_rule_at_t *at, lexbor_serialize_cb_f cb, + void *ctx) +{ + lxb_status_t status; + const lxb_css_at_rule__undef_t *undef; + const lxb_css_at_rule__custom_t *custom; + const lxb_css_entry_at_rule_data_t *data, *undata; + + static const lxb_char_t at_str[] = "@"; + + data = lxb_css_at_rule_by_id(at->type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, at_str, (sizeof(at_str) - 1), ctx, status); + + if (at->type == LXB_CSS_AT_RULE__UNDEF) { + undef = at->u.undef; + + undata = lxb_css_at_rule_by_id(undef->type); + if (undata == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, undata->name, undata->length, ctx, status); + } + else if (at->type == LXB_CSS_AT_RULE__CUSTOM) { + custom = at->u.custom; + + lexbor_serialize_write(cb, custom->name.data, + custom->name.length, ctx, status); + } + else { + lexbor_serialize_write(cb, data->name, data->length, ctx, status); + } + + return data->serialize(at->u.user, cb, ctx); +} + +lxb_status_t +lxb_css_rule_at_serialize_name(const lxb_css_rule_at_t *at, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_at_rule_serialize_name(at->u.user, at->type, cb, ctx); +} + +lxb_css_rule_style_t * +lxb_css_rule_style_destroy(lxb_css_rule_style_t *style, bool self_destroy) +{ + lxb_css_memory_t *memory = lxb_css_rule(style)->memory; + + lxb_css_selector_list_destroy(style->selector); + (void) lxb_css_rule_declaration_list_destroy(style->declarations, true); + (void) lxb_css_rule_list_destroy(style->child, true); + + style->selector = NULL; + style->declarations = NULL; + style->child = NULL; + + if (self_destroy) { + return lexbor_mraw_free(memory->tree, style); + } + + return style; +} + +lxb_status_t +lxb_css_rule_style_serialize(const lxb_css_rule_style_t *style, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + static const lxb_char_t lc_str[] = " {"; + static const lxb_char_t rc_str[] = "}"; + static const lxb_char_t cm_str[] = "; "; + + status = lxb_css_selector_serialize_list_chain(style->selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (style->declarations != NULL) { + lexbor_serialize_write(cb, lc_str, (sizeof(lc_str) - 1), ctx, status); + + status = lxb_css_rule_declaration_list_serialize(style->declarations, + cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (style->child == NULL) { + lexbor_serialize_write(cb, rc_str, (sizeof(rc_str) - 1), + ctx, status); + } + } + + if (style->child != NULL && style->child->first != NULL) { + lexbor_serialize_write(cb, cm_str, (sizeof(cm_str) - 1), ctx, status); + + status = lxb_css_rule_list_serialize(style->child, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + if (style->declarations != NULL) { + lexbor_serialize_write(cb, rc_str, (sizeof(rc_str) - 1), ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_css_rule_bad_style_t * +lxb_css_rule_bad_style_destroy(lxb_css_rule_bad_style_t *bad, bool self_destroy) +{ + lxb_css_memory_t *memory = lxb_css_rule(bad)->memory; + + (void) lexbor_str_destroy(&bad->selectors, memory->mraw, false); + bad->declarations = lxb_css_rule_declaration_list_destroy(bad->declarations, + true); + bad->child = lxb_css_rule_list_destroy(bad->child, true); + + if (self_destroy) { + return lexbor_mraw_free(memory->tree, bad); + } + + return bad; +} + +lxb_status_t +lxb_css_rule_bad_style_serialize(const lxb_css_rule_bad_style_t *bad, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + static const lxb_char_t lc_str[] = "{"; + static const lxb_char_t rc_str[] = "}"; + + if (bad->selectors.data != NULL) { + lexbor_serialize_write(cb, bad->selectors.data, bad->selectors.length, + ctx, status); + } + + if (bad->declarations != NULL) { + lexbor_serialize_write(cb, lc_str, (sizeof(lc_str) - 1), ctx, status); + + status = lxb_css_rule_declaration_list_serialize(bad->declarations, + cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(rc_str, (sizeof(rc_str) - 1), ctx); + } + + return LXB_STATUS_OK; +} + +lxb_css_rule_declaration_list_t * +lxb_css_rule_declaration_list_destroy(lxb_css_rule_declaration_list_t *list, + bool self_destroy) +{ + lxb_css_rule_t *declr, *next; + + if (list == NULL) { + return NULL; + } + + declr = list->first; + + while (declr != NULL) { + next = declr->next; + (void) lxb_css_rule_destroy(declr, true); + declr = next; + } + + if (self_destroy) { + return lexbor_mraw_free(lxb_css_rule(list)->memory->tree, list); + } + + list->first = NULL; + list->last = NULL; + + return list; +} + +lxb_status_t +lxb_css_rule_declaration_list_serialize(const lxb_css_rule_declaration_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_rule_t *declr; + + static const lxb_char_t sm_str[] = "; "; + + declr = list->first; + + if (declr == NULL) { + return LXB_STATUS_OK; + } + + status = lxb_css_rule_serialize(declr, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + declr = declr->next; + + while (declr != NULL) { + lexbor_serialize_write(cb, sm_str, (sizeof(sm_str) - 1), ctx, status); + + status = lxb_css_rule_serialize(declr, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + declr = declr->next; + } + + return LXB_STATUS_OK; +} + +lxb_css_rule_declaration_t * +lxb_css_rule_declaration_destroy(lxb_css_rule_declaration_t *declr, + bool self_destroy) +{ + lxb_css_memory_t *memory = lxb_css_rule(declr)->memory; + + if (declr->u.user != NULL) { + declr->u.user = lxb_css_property_destroy(memory, declr->u.user, + declr->type, true); + } + + if (self_destroy) { + return lexbor_mraw_free(memory->tree, declr); + } + + return declr; +} + +lxb_status_t +lxb_css_rule_declaration_serialize(const lxb_css_rule_declaration_t *declaration, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + const lxb_css_entry_data_t *data, *undata; + const lxb_css_property__undef_t *undef; + const lxb_css_property__custom_t *custom; + + static const lxb_char_t cl_str[] = ": "; + static const lxb_char_t imp_str[] = " !important"; + + data = lxb_css_property_by_id(declaration->type); + if (data == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + if (declaration->type == LXB_CSS_PROPERTY__UNDEF) { + undef = declaration->u.undef; + + if (undef->type > LXB_CSS_PROPERTY__CUSTOM) { + undata = lxb_css_property_by_id(undef->type); + if (undata == NULL) { + return LXB_STATUS_ERROR_NOT_EXISTS; + } + + lexbor_serialize_write(cb, undata->name, undata->length, ctx, status); + lexbor_serialize_write(cb, cl_str, (sizeof(cl_str) - 1), ctx, status); + } + } + else if (declaration->type == LXB_CSS_PROPERTY__CUSTOM) { + custom = declaration->u.custom; + + lexbor_serialize_write(cb, custom->name.data, custom->name.length, + ctx, status); + lexbor_serialize_write(cb, cl_str, (sizeof(cl_str) - 1), ctx, status); + } + else { + lexbor_serialize_write(cb, data->name, data->length, ctx, status); + lexbor_serialize_write(cb, cl_str, (sizeof(cl_str) - 1), ctx, status); + } + + status = data->serialize(declaration->u.user, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (declaration->important && declaration->type != LXB_CSS_PROPERTY__UNDEF) { + lexbor_serialize_write(cb, imp_str, (sizeof(imp_str) - 1), ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_rule_declaration_serialize_name(const lxb_css_rule_declaration_t *declr, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_property_serialize_name(declr->u.user, declr->type, cb, ctx); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.h new file mode 100644 index 0000000..308dced --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/rule.h @@ -0,0 +1,453 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_RULE_H +#define LXB_CSS_RULE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/at_rule.h" +#include "lexbor/css/property.h" +#include "lexbor/css/selectors/selectors.h" + + +#define lxb_css_rule(rule) ((lxb_css_rule_t *) (rule)) +#define lxb_css_rule_list(rule) ((lxb_css_rule_list_t *) (rule)) +#define lxb_css_rule_at(rule) ((lxb_css_rule_at_t *) (rule)) +#define lxb_css_rule_style(rule) ((lxb_css_rule_style_t *) (rule)) +#define lxb_css_rule_bad_style(rule) ((lxb_css_rule_bad_style_t *) (rule)) +#define lxb_css_rule_declaration_list(rule) ((lxb_css_rule_declaration_list_t *) (rule)) +#define lxb_css_rule_declaration(rule) ((lxb_css_rule_declaration_t *) (rule)) + + +typedef enum { + LXB_CSS_RULE_UNDEF = 0, + LXB_CSS_RULE_STYLESHEET, + LXB_CSS_RULE_LIST, + LXB_CSS_RULE_AT_RULE, + LXB_CSS_RULE_STYLE, + LXB_CSS_RULE_BAD_STYLE, + LXB_CSS_RULE_DECLARATION_LIST, + LXB_CSS_RULE_DECLARATION +} +lxb_css_rule_type_t; + +typedef struct lxb_css_rule lxb_css_rule_t; + +struct lxb_css_rule { + lxb_css_rule_type_t type; + lxb_css_rule_t *next; + lxb_css_rule_t *prev; + lxb_css_rule_t *parent; + + lxb_css_memory_t *memory; + size_t ref_count; +}; + +struct lxb_css_rule_list { + lxb_css_rule_t rule; + + lxb_css_rule_t *first; + lxb_css_rule_t *last; +}; + +struct lxb_css_rule_at { + lxb_css_rule_t rule; + + uintptr_t type; + + union { + lxb_css_at_rule__undef_t *undef; + lxb_css_at_rule__custom_t *custom; + lxb_css_at_rule_font_face_t *font_face; + lxb_css_at_rule_media_t *media; + lxb_css_at_rule_namespace_t *ns; + void *user; + } u; + + size_t name_begin; + size_t prelude_begin; + size_t prelude_end; +}; + +struct lxb_css_rule_style { + lxb_css_rule_t rule; + + lxb_css_selector_list_t *selector; + lxb_css_rule_declaration_list_t *declarations; + lxb_css_rule_list_t *child; + + size_t prelude_begin; + size_t prelude_end; +}; + +struct lxb_css_rule_bad_style { + lxb_css_rule_t rule; + + lexbor_str_t selectors; + lxb_css_rule_declaration_list_t *declarations; + lxb_css_rule_list_t *child; + + size_t prelude_begin; + size_t prelude_end; +}; + +struct lxb_css_rule_declaration_list { + lxb_css_rule_t rule; + + lxb_css_rule_t *first; + lxb_css_rule_t *last; + + size_t count; +}; + +typedef struct { + size_t name_begin; + size_t name_end; + size_t value_begin; + size_t value_end; + size_t important_begin; + size_t important_end; +} +lxb_css_rule_declaration_offset_t; + +struct lxb_css_rule_declaration { + lxb_css_rule_t rule; + uintptr_t type; + + union { + lxb_css_property__undef_t *undef; + lxb_css_property__custom_t *custom; + lxb_css_property_display_t *display; + lxb_css_property_order_t *order; + lxb_css_property_visibility_t *visibility; + lxb_css_property_width_t *width; + lxb_css_property_height_t *height; + lxb_css_property_box_sizing_t *box_sizing; + lxb_css_property_margin_t *margin; + lxb_css_property_margin_top_t *margin_top; + lxb_css_property_margin_right_t *margin_right; + lxb_css_property_margin_bottom_t *margin_bottom; + lxb_css_property_margin_left_t *margin_left; + lxb_css_property_padding_t *padding; + lxb_css_property_padding_top_t *padding_top; + lxb_css_property_padding_right_t *padding_right; + lxb_css_property_padding_bottom_t *padding_bottom; + lxb_css_property_padding_left_t *padding_left; + lxb_css_property_border_t *border; + lxb_css_property_border_top_t *border_top; + lxb_css_property_border_right_t *border_right; + lxb_css_property_border_bottom_t *border_bottom; + lxb_css_property_border_left_t *border_left; + lxb_css_property_border_top_color_t *border_top_color; + lxb_css_property_border_right_color_t *border_right_color; + lxb_css_property_border_bottom_color_t *border_bottom_color; + lxb_css_property_border_left_color_t *border_left_color; + lxb_css_property_background_color_t *background_color; + lxb_css_property_color_t *color; + lxb_css_property_opacity_t *opacity; + lxb_css_property_position_t *position; + lxb_css_property_top_t *top; + lxb_css_property_right_t *right; + lxb_css_property_bottom_t *bottom; + lxb_css_property_left_t *left; + lxb_css_property_inset_block_start_t *inset_block_start; + lxb_css_property_inset_inline_start_t *inset_inline_start; + lxb_css_property_inset_block_end_t *inset_block_end; + lxb_css_property_inset_inline_end_t *inset_inline_end; + lxb_css_property_text_transform_t *text_transform; + lxb_css_property_text_align_t *text_align; + lxb_css_property_text_align_all_t *text_align_all; + lxb_css_property_text_align_last_t *text_align_last; + lxb_css_property_text_justify_t *text_justify; + lxb_css_property_text_indent_t *text_indent; + lxb_css_property_white_space_t *white_space; + lxb_css_property_tab_size_t *tab_size; + lxb_css_property_word_break_t *word_break; + lxb_css_property_line_break_t *line_break; + lxb_css_property_hyphens_t *hyphens; + lxb_css_property_overflow_wrap_t *overflow_wrap; + lxb_css_property_word_wrap_t *word_wrap; + lxb_css_property_word_spacing_t *word_spacing; + lxb_css_property_letter_spacing_t *letter_spacing; + lxb_css_property_hanging_punctuation_t *hanging_punctuation; + lxb_css_property_font_family_t *font_family; + lxb_css_property_font_weight_t *font_weight; + lxb_css_property_font_stretch_t *font_stretch; + lxb_css_property_font_style_t *font_style; + lxb_css_property_font_size_t *font_size; + lxb_css_property_float_reference_t *float_reference; + lxb_css_property_float_t *floatp; + lxb_css_property_clear_t *clear; + lxb_css_property_float_defer_t *float_defer; + lxb_css_property_float_offset_t *float_offset; + lxb_css_property_wrap_flow_t *wrap_flow; + lxb_css_property_wrap_through_t *wrap_through; + lxb_css_property_flex_direction_t *flex_direction; + lxb_css_property_flex_wrap_t *flex_wrap; + lxb_css_property_flex_flow_t *flex_flow; + lxb_css_property_flex_t *flex; + lxb_css_property_flex_grow_t *flex_grow; + lxb_css_property_flex_shrink_t *flex_shrink; + lxb_css_property_flex_basis_t *flex_basis; + lxb_css_property_justify_content_t *justify_content; + lxb_css_property_align_items_t *align_items; + lxb_css_property_align_self_t *align_self; + lxb_css_property_align_content_t *align_content; + lxb_css_property_dominant_baseline_t *dominant_baseline; + lxb_css_property_vertical_align_t *vertical_align; + lxb_css_property_baseline_source_t *baseline_source; + lxb_css_property_alignment_baseline_t *alignment_baseline; + lxb_css_property_baseline_shift_t *baseline_shift; + lxb_css_property_line_height_t *line_height; + lxb_css_property_z_index_t *z_index; + lxb_css_property_direction_t *direction; + lxb_css_property_unicode_bidi_t *unicode_bidi; + lxb_css_property_writing_mode_t *writing_mode; + lxb_css_property_text_orientation_t *text_orientation; + lxb_css_property_text_combine_upright_t *text_combine_upright; + lxb_css_property_overflow_x_t *overflow_x; + lxb_css_property_overflow_y_t *overflow_y; + lxb_css_property_overflow_block_t *overflow_block; + lxb_css_property_overflow_inline_t *overflow_inline; + lxb_css_property_text_overflow_t *text_overflow; + lxb_css_property_text_decoration_line_t *text_decoration_line; + lxb_css_property_text_decoration_style_t *text_decoration_style; + lxb_css_property_text_decoration_color_t *text_decoration_color; + lxb_css_property_text_decoration_t *text_decoration; + void *user; + } u; + + lxb_css_rule_declaration_offset_t offset; + + bool important; +}; + + +LXB_API void * +lxb_css_rule_destroy(lxb_css_rule_t *node, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_serialize(const lxb_css_rule_t *rule, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_rule_serialize_chain(const lxb_css_rule_t *rule, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_css_rule_list_t * +lxb_css_rule_list_destroy(lxb_css_rule_list_t *list, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_list_serialize(const lxb_css_rule_list_t *list, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_css_rule_at_t * +lxb_css_rule_at_destroy(lxb_css_rule_at_t *at, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_at_serialize(const lxb_css_rule_at_t *at, lexbor_serialize_cb_f cb, + void *ctx); + +LXB_API lxb_status_t +lxb_css_rule_at_serialize_name(const lxb_css_rule_at_t *at, lexbor_serialize_cb_f cb, + void *ctx); + +LXB_API lxb_css_rule_style_t * +lxb_css_rule_style_destroy(lxb_css_rule_style_t *style, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_style_serialize(const lxb_css_rule_style_t *style, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_css_rule_bad_style_t * +lxb_css_rule_bad_style_destroy(lxb_css_rule_bad_style_t *bad, bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_bad_style_serialize(const lxb_css_rule_bad_style_t *bad, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_css_rule_declaration_list_t * +lxb_css_rule_declaration_list_destroy(lxb_css_rule_declaration_list_t *list, + bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_declaration_list_serialize(const lxb_css_rule_declaration_list_t *list, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_css_rule_declaration_t * +lxb_css_rule_declaration_destroy(lxb_css_rule_declaration_t *declr, + bool self_destroy); + +LXB_API lxb_status_t +lxb_css_rule_declaration_serialize(const lxb_css_rule_declaration_t *declaration, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_rule_declaration_serialize_name(const lxb_css_rule_declaration_t *declr, + lexbor_serialize_cb_f cb, void *ctx); + +/* + * Inline functions + */ +lxb_inline void +lxb_css_rule_insert_next(lxb_css_rule_t *root, lxb_css_rule_t *rule) +{ + if (root->next != NULL) { + rule->next = root->next; + } + + root->next = rule; + rule->prev = root; +} + +lxb_inline lxb_css_rule_t * +lxb_css_rule_create(lxb_css_memory_t *memory, size_t size, + lxb_css_rule_type_t type) +{ + lxb_css_rule_t *rule; + + rule = (lxb_css_rule_t *) lexbor_mraw_calloc(memory->tree, size); + if (rule== NULL) { + return NULL; + } + + rule->type = type; + rule->memory = memory; + + return rule; +} + +lxb_inline lxb_css_rule_list_t * +lxb_css_rule_list_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_list_t *) lxb_css_rule_create(memory, + sizeof(lxb_css_rule_list_t), + LXB_CSS_RULE_LIST); +} + +lxb_inline size_t +lxb_css_rule_ref_count(lxb_css_rule_t *rule) +{ + return rule->ref_count; +} + +lxb_inline lxb_status_t +lxb_css_rule_ref_inc(lxb_css_rule_t *rule) +{ + if (SIZE_MAX - rule->ref_count == 0) { + return LXB_STATUS_ERROR_OVERFLOW; + } + + rule->ref_count++; + + return LXB_STATUS_OK; +} + +lxb_inline void +lxb_css_rule_ref_dec(lxb_css_rule_t *rule) +{ + if (rule->ref_count > 0) { + rule->ref_count--; + } +} + +lxb_inline void +lxb_css_rule_ref_dec_destroy(lxb_css_rule_t *rule) +{ + if (rule->ref_count > 0) { + rule->ref_count--; + } + + if (rule->ref_count == 0) { + (void) lxb_css_rule_destroy(rule, true); + } +} + +lxb_inline void +lxb_css_rule_list_append(lxb_css_rule_list_t *list, lxb_css_rule_t *rule) +{ + if (list->first == NULL) { + list->first = rule; + } + else { + lxb_css_rule_insert_next(list->last, rule); + } + + list->last = rule; + rule->parent = lxb_css_rule(list); + + (void) lxb_css_rule_ref_inc(rule); +} + +lxb_inline lxb_css_rule_at_t * +lxb_css_rule_at_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_at_t *) + lxb_css_rule_create(memory, sizeof(lxb_css_rule_at_t), + LXB_CSS_RULE_AT_RULE); +} + +lxb_inline lxb_css_rule_style_t * +lxb_css_rule_style_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_style_t *) + lxb_css_rule_create(memory, sizeof(lxb_css_rule_style_t), + LXB_CSS_RULE_STYLE); +} + +lxb_inline lxb_css_rule_bad_style_t * +lxb_css_rule_bad_style_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_bad_style_t *) + lxb_css_rule_create(memory, sizeof(lxb_css_rule_bad_style_t), + LXB_CSS_RULE_BAD_STYLE); +} + +lxb_inline lxb_css_rule_declaration_list_t * +lxb_css_rule_declaration_list_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_declaration_list_t *) + lxb_css_rule_create(memory, sizeof(lxb_css_rule_declaration_list_t), + LXB_CSS_RULE_DECLARATION_LIST); +} + +lxb_inline void +lxb_css_rule_declaration_list_append(lxb_css_rule_declaration_list_t *list, + lxb_css_rule_t *rule) +{ + if (list->first == NULL) { + list->first = rule; + } + else { + lxb_css_rule_insert_next(list->last, rule); + } + + list->last = rule; + rule->parent = lxb_css_rule(list); + + (void) lxb_css_rule_ref_inc(rule); + + list->count++; +} + +lxb_inline lxb_css_rule_declaration_t * +lxb_css_rule_declaration_create(lxb_css_memory_t *memory) +{ + return (lxb_css_rule_declaration_t *) + lxb_css_rule_create(memory, sizeof(lxb_css_rule_declaration_t), + LXB_CSS_RULE_DECLARATION); +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_RULE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/base.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/base.h new file mode 100644 index 0000000..3151f74 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/base.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2021-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_BASE_H +#define LEXBOR_CSS_SELECTORS_BASE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/core/base.h" + + +#define LXB_CSS_SELECTORS_VERSION_MAJOR 1 +#define LXB_CSS_SELECTORS_VERSION_MINOR 3 +#define LXB_CSS_SELECTORS_VERSION_PATCH 0 + +#define LXB_CSS_SELECTORS_VERSION_STRING \ + LEXBOR_STRINGIZE(LXB_CSS_SELECTORS_VERSION_MAJOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_SELECTORS_VERSION_MINOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_SELECTORS_VERSION_PATCH) + + +#define lxb_css_selector_serialize_write(data, length) \ + do { \ + (status) = cb((lxb_char_t *) (data), (length), (ctx)); \ + if ((status) != LXB_STATUS_OK) { \ + return (status); \ + } \ + } \ + while (false) + + +typedef struct lxb_css_selectors lxb_css_selectors_t; +typedef struct lxb_css_selector lxb_css_selector_t; +typedef struct lxb_css_selector_list lxb_css_selector_list_t; + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_BASE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.c new file mode 100644 index 0000000..0468682 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/state.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/pseudo_res.h" + + +const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_class_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_class_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_name(const lxb_char_t *name, + size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_class_function_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_id(unsigned id) +{ + return &lxb_css_selectors_pseudo_data_pseudo_class_function[id]; +} + +const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_element_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_element_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_name(const lxb_char_t *name, + size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_selectors_pseudo_element_function_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_id(unsigned id) +{ + return &lxb_css_selectors_pseudo_data_pseudo_element_function[id]; +} + +const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_function_by_id(unsigned id, bool is_class) +{ + if (is_class) { + return &lxb_css_selectors_pseudo_data_pseudo_class_function[id]; + } + + return &lxb_css_selectors_pseudo_data_pseudo_element_function[id]; +} + +bool +lxb_css_selector_pseudo_function_can_empty(unsigned id, bool is_class) +{ + if (is_class) { + return lxb_css_selectors_pseudo_data_pseudo_class_function[id].empty; + } + + return lxb_css_selectors_pseudo_data_pseudo_element_function[id].empty; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.h new file mode 100644 index 0000000..13eef4c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_PSEUDO_H +#define LEXBOR_CSS_SELECTORS_PSEUDO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/selectors/base.h" + + +typedef struct { + lxb_char_t *name; + size_t length; + unsigned id; + bool empty; + lxb_css_selector_combinator_t combinator; + const lxb_css_syntax_cb_function_t cb; + bool forgiving; + bool comma; +} +lxb_css_selectors_pseudo_data_func_t; + +typedef struct { + lxb_char_t *name; + size_t length; + unsigned id; +} +lxb_css_selectors_pseudo_data_t; + + +LXB_API const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_class_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_name(const lxb_char_t *name, + size_t length); + +LXB_API const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_class_function_by_id(unsigned id); + +LXB_API const lxb_css_selectors_pseudo_data_t * +lxb_css_selector_pseudo_element_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_name(const lxb_char_t *name, + size_t length); + +LXB_API const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_element_function_by_id(unsigned id); + +LXB_API const lxb_css_selectors_pseudo_data_func_t * +lxb_css_selector_pseudo_function_by_id(unsigned id, bool is_class); + +LXB_API bool +lxb_css_selector_pseudo_function_can_empty(unsigned id, bool is_class); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_PSEUDO_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_const.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_const.h new file mode 100644 index 0000000..9fa5bb2 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_const.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020-2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/selectors/pseudo.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_SELECTOS_PSEUDO_CONST_H +#define LXB_CSS_SELECTOS_PSEUDO_CONST_H + + +typedef enum { + LXB_CSS_SELECTOR_PSEUDO_CLASS__UNDEF = 0x0000, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE = 0x0001, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK = 0x0002, + LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK = 0x0003, + LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED = 0x0004, + LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT = 0x0005, + LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT = 0x0006, + LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED = 0x0007, + LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY = 0x0008, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED = 0x0009, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD = 0x000a, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE = 0x000b, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS = 0x000c, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE = 0x000d, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN = 0x000e, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN = 0x000f, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE = 0x0010, + LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER = 0x0011, + LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE = 0x0012, + LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE = 0x0013, + LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID = 0x0014, + LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD = 0x0015, + LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE = 0x0016, + LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK = 0x0017, + LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK = 0x0018, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD = 0x0019, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE = 0x001a, + LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL = 0x001b, + LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE = 0x001c, + LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST = 0x001d, + LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN = 0x001e, + LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY = 0x001f, + LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE = 0x0020, + LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED = 0x0021, + LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT = 0x0022, + LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE = 0x0023, + LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET = 0x0024, + LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN = 0x0025, + LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID = 0x0026, + LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID = 0x0027, + LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED = 0x0028, + LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING = 0x0029, + LXB_CSS_SELECTOR_PSEUDO_CLASS__LAST_ENTRY = 0x002a +} +lxb_css_selector_pseudo_class_id_t; + +typedef enum { + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION__UNDEF = 0x0000, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT = 0x0001, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR = 0x0002, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS = 0x0003, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS = 0x0004, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG = 0x0005, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LEXBOR_CONTAINS = 0x0006, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT = 0x0007, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD = 0x0008, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL = 0x0009, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD = 0x000a, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL = 0x000b, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE = 0x000c, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE = 0x000d, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE = 0x000e, + LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION__LAST_ENTRY = 0x000f +} +lxb_css_selector_pseudo_class_function_id_t; + +typedef enum { + LXB_CSS_SELECTOR_PSEUDO_ELEMENT__UNDEF = 0x0000, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER = 0x0001, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP = 0x0002, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE = 0x0003, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER = 0x0004, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE = 0x0005, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR = 0x0006, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION = 0x0007, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER = 0x0008, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER = 0x0009, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION = 0x000a, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR = 0x000b, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT = 0x000c, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT__LAST_ENTRY = 0x000d +} +lxb_css_selector_pseudo_element_id_t; + +typedef enum { + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FUNCTION__UNDEF = 0x0000, + LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FUNCTION__LAST_ENTRY = 0x0001 +} +lxb_css_selector_pseudo_element_function_id_t; + + +#endif /* LXB_CSS_SELECTOS_PSEUDO_CONST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_res.h new file mode 100644 index 0000000..1082574 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_res.h @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/selectors/pseudo.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_SELECTOS_PSEUDO_RES_H +#define LXB_CSS_SELECTOS_PSEUDO_RES_H + +#include "lexbor/core/shs.h" +#include "lexbor/css/selectors/pseudo_const.h" + + +static const lxb_css_selectors_pseudo_data_t lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_SELECTOR_PSEUDO_CLASS__UNDEF}, + {(lxb_char_t *) "active", 6, LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE}, + {(lxb_char_t *) "any-link", 8, LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK}, + {(lxb_char_t *) "blank", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK}, + {(lxb_char_t *) "checked", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED}, + {(lxb_char_t *) "current", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT}, + {(lxb_char_t *) "default", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT}, + {(lxb_char_t *) "disabled", 8, LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED}, + {(lxb_char_t *) "empty", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY}, + {(lxb_char_t *) "enabled", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED}, + {(lxb_char_t *) "first-child", 11, LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD}, + {(lxb_char_t *) "first-of-type", 13, LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE}, + {(lxb_char_t *) "focus", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS}, + {(lxb_char_t *) "focus-visible", 13, LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE}, + {(lxb_char_t *) "focus-within", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN}, + {(lxb_char_t *) "fullscreen", 10, LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN}, + {(lxb_char_t *) "future", 6, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE}, + {(lxb_char_t *) "hover", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER}, + {(lxb_char_t *) "in-range", 8, LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE}, + {(lxb_char_t *) "indeterminate", 13, LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE}, + {(lxb_char_t *) "invalid", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID}, + {(lxb_char_t *) "last-child", 10, LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD}, + {(lxb_char_t *) "last-of-type", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE}, + {(lxb_char_t *) "link", 4, LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK}, + {(lxb_char_t *) "local-link", 10, LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK}, + {(lxb_char_t *) "only-child", 10, LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD}, + {(lxb_char_t *) "only-of-type", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE}, + {(lxb_char_t *) "optional", 8, LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL}, + {(lxb_char_t *) "out-of-range", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE}, + {(lxb_char_t *) "past", 4, LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST}, + {(lxb_char_t *) "placeholder-shown", 17, LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN}, + {(lxb_char_t *) "read-only", 9, LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY}, + {(lxb_char_t *) "read-write", 10, LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE}, + {(lxb_char_t *) "required", 8, LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED}, + {(lxb_char_t *) "root", 4, LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT}, + {(lxb_char_t *) "scope", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE}, + {(lxb_char_t *) "target", 6, LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET}, + {(lxb_char_t *) "target-within", 13, LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN}, + {(lxb_char_t *) "user-invalid", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID}, + {(lxb_char_t *) "valid", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID}, + {(lxb_char_t *) "visited", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED}, + {(lxb_char_t *) "warning", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING} +}; + +static const lxb_css_selectors_pseudo_data_func_t lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION__UNDEF, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function__undef, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "current", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_current, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, true}, + {(lxb_char_t *) "dir", 3, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_dir, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "has", 3, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS, + false, LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT, + {.value = lxb_css_selectors_state_pseudo_class_function_has, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_forgiving_relative}, true, true}, + {(lxb_char_t *) "is", 2, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_is, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_forgiving}, true, true}, + {(lxb_char_t *) "lang", 4, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_lang, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "lexbor-contains", 15, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LEXBOR_CONTAINS, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_lexbor_contains, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "not", 3, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_not, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, true}, + {(lxb_char_t *) "nth-child", 9, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_child, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, true}, + {(lxb_char_t *) "nth-col", 7, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_col, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "nth-last-child", 14, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_last_child, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, true}, + {(lxb_char_t *) "nth-last-col", 12, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_last_col, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "nth-last-of-type", 16, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_last_of_type, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "nth-of-type", 11, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_nth_of_type, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false}, + {(lxb_char_t *) "where", 5, LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_class_function_where, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_forgiving}, true, true} +}; + +static const lxb_css_selectors_pseudo_data_t lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_SELECTOR_PSEUDO_ELEMENT__UNDEF}, + {(lxb_char_t *) "after", 5, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER}, + {(lxb_char_t *) "backdrop", 8, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP}, + {(lxb_char_t *) "before", 6, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE}, + {(lxb_char_t *) "first-letter", 12, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER}, + {(lxb_char_t *) "first-line", 10, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE}, + {(lxb_char_t *) "grammar-error", 13, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR}, + {(lxb_char_t *) "inactive-selection", 18, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION}, + {(lxb_char_t *) "marker", 6, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER}, + {(lxb_char_t *) "placeholder", 11, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER}, + {(lxb_char_t *) "selection", 9, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION}, + {(lxb_char_t *) "spelling-error", 14, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR}, + {(lxb_char_t *) "target-text", 11, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT} +}; + +static const lxb_css_selectors_pseudo_data_func_t lxb_css_selectors_pseudo_data_pseudo_element_function[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FUNCTION__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FUNCTION__UNDEF, + false, LXB_CSS_SELECTOR_COMBINATOR_CLOSE, + {.value = lxb_css_selectors_state_pseudo_element_function__undef, + .cb.failed = lxb_css_state_failed, .cb.end = lxb_css_selectors_state_function_end}, false, false} +}; + +static const lexbor_shs_entry_t lxb_css_selectors_pseudo_class_shs[79] = +{ + {NULL, NULL, 78, 0}, + {"focus-visible", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_VISIBLE], 13, 0}, + {"first-of-type", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_OF_TYPE], 13, 1}, + {"in-range", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_IN_RANGE], 8, 0}, + {"out-of-range", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_OUT_OF_RANGE], 12, 0}, + {"read-write", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_WRITE], 10, 0}, + {"hover", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_HOVER], 5, 3}, + {"enabled", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ENABLED], 7, 0}, + {"fullscreen", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FULLSCREEN], 10, 0}, + {"placeholder-shown", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_PLACEHOLDER_SHOWN], 17, 0}, + {"target-within", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET_WITHIN], 13, 0}, + {"indeterminate", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_INDETERMINATE], 13, 0}, + {"local-link", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_LOCAL_LINK], 10, 0}, + {"user-invalid", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_USER_INVALID], 12, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"past", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_PAST], 4, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"only-of-type", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_OF_TYPE], 12, 4}, + {"only-child", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ONLY_CHILD], 10, 5}, + {"focus", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS], 5, 7}, + {NULL, NULL, 0, 0}, + {"any-link", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ANY_LINK], 8, 0}, + {NULL, NULL, 0, 0}, + {"valid", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_VALID], 5, 0}, + {NULL, NULL, 0, 0}, + {"visited", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_VISITED], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"root", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ROOT], 4, 8}, + {NULL, NULL, 0, 0}, + {"focus-within", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FOCUS_WITHIN], 12, 0}, + {"checked", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_CHECKED], 7, 9}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"active", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_ACTIVE], 6, 0}, + {"last-of-type", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_OF_TYPE], 12, 0}, + {NULL, NULL, 0, 0}, + {"target", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_TARGET], 6, 0}, + {"read-only", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_READ_ONLY], 9, 10}, + {NULL, NULL, 0, 0}, + {"first-child", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FIRST_CHILD], 11, 0}, + {"disabled", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_DISABLED], 8, 0}, + {NULL, NULL, 0, 0}, + {"required", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_REQUIRED], 8, 0}, + {NULL, NULL, 0, 0}, + {"link", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_LINK], 4, 0}, + {NULL, NULL, 0, 0}, + {"empty", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_EMPTY], 5, 0}, + {"invalid", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_INVALID], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"scope", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_SCOPE], 5, 12}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"blank", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_BLANK], 5, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"warning", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_WARNING], 7, 0}, + {"default", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_DEFAULT], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"future", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUTURE], 6, 0}, + {"current", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_CURRENT], 7, 0}, + {"optional", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_OPTIONAL], 8, 0}, + {NULL, NULL, 0, 0}, + {"last-child", (void *) &lxb_css_selectors_pseudo_data_pseudo_class[LXB_CSS_SELECTOR_PSEUDO_CLASS_LAST_CHILD], 10, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_selectors_pseudo_class_function_shs[43] = +{ + {NULL, NULL, 42, 0}, + {"nth-last-of-type", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE], 16, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"not", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"has", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"nth-col", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL], 7, 0}, + {NULL, NULL, 0, 0}, + {"lexbor-contains", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LEXBOR_CONTAINS], 15, 0}, + {NULL, NULL, 0, 0}, + {"is", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS], 2, 0}, + {"nth-last-col", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL], 12, 0}, + {"current", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT], 7, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"lang", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG], 4, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"nth-child", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD], 9, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"nth-last-child", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD], 14, 0}, + {"nth-of-type", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE], 11, 0}, + {NULL, NULL, 0, 0}, + {"dir", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR], 3, 0}, + {"where", (void *) &lxb_css_selectors_pseudo_data_pseudo_class_function[LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE], 5, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_selectors_pseudo_element_shs[24] = +{ + {NULL, NULL, 23, 0}, + {NULL, NULL, 0, 0}, + {"inactive-selection", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION], 18, 0}, + {"placeholder", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER], 11, 0}, + {"after", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER], 5, 0}, + {"first-letter", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER], 12, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"grammar-error", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR], 13, 0}, + {"before", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE], 6, 0}, + {"selection", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION], 9, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"target-text", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT], 11, 0}, + {"first-line", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE], 10, 0}, + {"spelling-error", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR], 14, 0}, + {"backdrop", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP], 8, 0}, + {"marker", (void *) &lxb_css_selectors_pseudo_data_pseudo_element[LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER], 6, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_selectors_pseudo_element_function_shs[6] = +{ + {NULL, NULL, 5, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + + +#endif /* LXB_CSS_SELECTOS_PSEUDO_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.c new file mode 100644 index 0000000..dc7dddc --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.c @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/selectors.h" + + +static bool +lxb_css_selectors_state_pseudo_anb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static bool +lxb_css_selectors_state_pseudo_of_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_selectors_state_pseudo_of_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static bool +lxb_css_selectors_state_pseudo_of_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_comp = { + .prelude = lxb_css_selectors_state_complex_list, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_state_pseudo_of_end +}; + + +lxb_inline bool +lxb_css_selectors_state_pseudo_anb_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + (void) lxb_css_selectors_state_pseudo_anb(parser, token, ctx); + if (parser->status != LXB_STATUS_OK) { + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return lxb_css_parser_failed(parser); + } + + parser->selectors->list = NULL; + + return lxb_css_parser_success(parser); +} + + +bool +lxb_css_selectors_state_pseudo_class_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_current(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_dir(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_has(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_relative_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_is(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_lang(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_fail(parser, LXB_STATUS_ERROR_UNEXPECTED_DATA); +} + +bool +lxb_css_selectors_state_pseudo_class_function_not(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_of_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_of_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_last_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_nth_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_selectors_state_pseudo_anb_begin(parser, token, ctx); +} + +bool +lxb_css_selectors_state_pseudo_class_function_where(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + parser->selectors->list = NULL; + parser->selectors->list_last = NULL; + + return true; +} + +bool +lxb_css_selectors_state_pseudo_element_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return true; +} + +bool +lxb_css_selectors_state_pseudo_class_function_lexbor_contains(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_selectors_t *selectors; + lxb_css_selector_t *selector; + lxb_css_selector_contains_t *contains; + lexbor_str_t *str; + const lxb_char_t *data; + size_t length; + + selectors = parser->selectors; + selector = selectors->list_last->last; + +again: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_STRING: + data = token->types.string.data; + length = token->types.string.length; + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + data = token->types.ident.data; + length = token->types.ident.length; + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + goto again; + + default: + lxb_css_parser_unexpected_data(parser, token); + return lxb_css_parser_failed(parser); + } + + contains = lexbor_mraw_alloc(parser->memory->mraw, + sizeof(lxb_css_selector_contains_t)); + if (contains == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + contains->insensitive = false; + str = &contains->str; + + str->data = lexbor_mraw_alloc(parser->memory->mraw, length + 1); + if (str->data == NULL) { + lexbor_mraw_free(parser->memory->mraw, contains); + return lxb_css_parser_memory_fail(parser); + } + + memcpy(str->data, data, length); + + str->length = length; + str->data[length] = '\0'; + + selector->u.pseudo.data = contains; + +again_end: + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN__END: + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + goto again_end; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + data = token->types.ident.data; + length = token->types.ident.length; + + if (length == 1 && (*data == 'i' || *data == 'I')) { + contains->insensitive = true; + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + goto failed; + } + + break; + } + /* Fall through. */ + + default: + goto failed; + } + + parser->selectors->list = NULL; + + return lxb_css_parser_success(parser); + +failed: + + lexbor_mraw_free(parser->memory->mraw, contains->str.data); + lexbor_mraw_free(parser->memory->mraw, contains); + + selector->u.pseudo.data = NULL; + + lxb_css_parser_unexpected_data(parser, token); + return lxb_css_parser_failed(parser); +} + +static bool +lxb_css_selectors_state_pseudo_anb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *list; + lxb_css_selector_anb_of_t *anbof; + + selectors = parser->selectors; + + anbof = lexbor_mraw_alloc(parser->memory->mraw, + sizeof(lxb_css_selector_anb_of_t)); + if (anbof == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + parser->status = lxb_css_syntax_anb_handler(parser, token, &anbof->anb); + if (parser->status != LXB_STATUS_OK) { + lexbor_mraw_free(parser->memory->mraw, anbof); + return true; + } + + list = selectors->list_last; + list->last->u.pseudo.data = anbof; + + anbof->of = NULL; + + return true; +} + +static bool +lxb_css_selectors_state_pseudo_of_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_syntax_rule_t *rule; + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *list; + lxb_css_syntax_token_ident_t *ident; + + static const lxb_char_t of[] = "of"; + + selectors = parser->selectors; + + (void) lxb_css_selectors_state_pseudo_anb(parser, token, ctx); + if (parser->status != LXB_STATUS_OK) { + selectors->list = NULL; + selectors->list_last = NULL; + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + if (lxb_css_syntax_token_error(parser, token, "Selectors") == NULL) { + return lxb_css_parser_memory_fail(parser); + } + } + + return lxb_css_parser_failed(parser); + } + + list = selectors->list_last; + + selectors->list = NULL; + + lxb_css_parser_token_wo_ws_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + ident = lxb_css_syntax_token_ident(token); + + if (ident->length == sizeof(of) - 1 + && lexbor_str_data_ncasecmp(ident->data, of, ident->length)) + { + lxb_css_syntax_token_consume(parser->tkz); + + selectors->list = NULL; + selectors->list_last = NULL; + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + rule = lxb_css_syntax_parser_components_push(parser, + &lxb_css_selectors_comp, + lxb_css_selectors_state_pseudo_of_back, + list, LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); +// rule = lxb_css_syntax_parser_components_push(parser, token, +// lxb_css_selectors_state_pseudo_of_back, +// &lxb_css_selectors_comp, list, +// LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + if (rule == NULL) { + lexbor_mraw_free(parser->memory->mraw, + list->last->u.pseudo.data); + return lxb_css_parser_memory_fail(parser); + } + + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_complex_list); + return true; + } + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_of_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_selector_anb_of_t *anbof; + lxb_css_selector_list_t *list = ctx; + + anbof = list->last->u.pseudo.data; + anbof->of = parser->selectors->list; + + parser->selectors->list = NULL; + + return LXB_STATUS_OK; +} + +static bool +lxb_css_selectors_state_pseudo_of_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + parser->selectors->list = NULL; + + return lxb_css_parser_success(parser); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.h new file mode 100644 index 0000000..652e455 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/pseudo_state.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020-2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_PSEUDO_STATE_H +#define LEXBOR_CSS_SELECTORS_PSEUDO_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/selectors/base.h" + + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_current(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_dir(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_has(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_is(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_lang(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_not(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_last_child(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_last_col(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_last_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_nth_of_type(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_where(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_element_function__undef(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_pseudo_class_function_lexbor_contains(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_PSEUDO_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.c new file mode 100644 index 0000000..e29d27b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.c @@ -0,0 +1,1002 @@ +/* + * Copyright (C) 2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/serialize.h" +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/selector.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_const.h" +#include "lexbor/css/selectors/pseudo_state.h" +#include "lexbor/css/selectors/state.h" +#include "lexbor/css/selectors/pseudo_res.h" + + +typedef void +(*lxb_css_selector_destroy_f)(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +typedef lxb_status_t +(*lxb_css_selector_serialize_f)(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + + +static void +lxb_css_selector_destroy_undef(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_any(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_id(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_attribute(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_pseudo(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_pseudo_class_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); +static void +lxb_css_selector_destroy_pseudo_element_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem); + +static lxb_status_t +lxb_css_selector_serialize_undef(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_any(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_id(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_attribute(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_class_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_element(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); +static lxb_status_t +lxb_css_selector_serialize_pseudo_element_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + +static lxb_status_t +lxb_css_selector_serialize_pseudo_single(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx, + bool is_class); + + +static const lxb_css_selector_destroy_f + lxb_selector_destroy_map[LXB_CSS_SELECTOR_TYPE__LAST_ENTRY] = +{ + lxb_css_selector_destroy_undef, + lxb_css_selector_destroy_any, + lxb_css_selector_destroy_any, + lxb_css_selector_destroy_id, + lxb_css_selector_destroy_id, + lxb_css_selector_destroy_attribute, + lxb_css_selector_destroy_pseudo, + lxb_css_selector_destroy_pseudo_class_function, + lxb_css_selector_destroy_pseudo, + lxb_css_selector_destroy_pseudo_element_function +}; + +static const lxb_css_selector_serialize_f + lxb_selector_serialize_map[LXB_CSS_SELECTOR_TYPE__LAST_ENTRY] = +{ + lxb_css_selector_serialize_undef, + lxb_css_selector_serialize_any, + lxb_css_selector_serialize_any, + lxb_css_selector_serialize_id, + lxb_css_selector_serialize_class, + lxb_css_selector_serialize_attribute, + lxb_css_selector_serialize_pseudo_class, + lxb_css_selector_serialize_pseudo_class_function, + lxb_css_selector_serialize_pseudo_element, + lxb_css_selector_serialize_pseudo_element_function +}; + + +lxb_css_selector_t * +lxb_css_selector_create(lxb_css_selector_list_t *list) +{ + lxb_css_selector_t *selector = lexbor_dobject_calloc(list->memory->objs); + if (selector == NULL) { + return NULL; + } + + selector->list = list; + + return selector; +} + +void +lxb_css_selector_destroy(lxb_css_selector_t *selector) +{ + lxb_css_memory_t *memory; + + if (selector != NULL) { + memory = selector->list->memory; + + lxb_selector_destroy_map[selector->type](selector, memory); + lexbor_dobject_free(memory->objs, selector); + } +} + +void +lxb_css_selector_destroy_chain(lxb_css_selector_t *selector) +{ + lxb_css_selector_t *next; + + while (selector != NULL) { + next = selector->next; + lxb_css_selector_destroy(selector); + selector = next; + } +} + +void +lxb_css_selector_remove(lxb_css_selector_t *selector) +{ + if (selector->next != NULL) { + selector->next->prev = selector->prev; + } + + if (selector->prev != NULL) { + selector->prev->next = selector->next; + } + + if (selector->list->first == selector) { + selector->list->first = selector->next; + } + + if (selector->list->last == selector) { + selector->list->last = selector->prev; + } +} + +lxb_css_selector_list_t * +lxb_css_selector_list_create(lxb_css_memory_t *mem) +{ + lxb_css_selector_list_t *list; + + list = lexbor_dobject_calloc(mem->objs); + if (list == NULL) { + return NULL; + } + + list->memory = mem; + + return list; +} + +void +lxb_css_selector_list_remove(lxb_css_selector_list_t *list) +{ + if (list->next != NULL) { + list->next->prev = list->prev; + } + + if (list->prev != NULL) { + list->prev->next = list->next; + } +} + +void +lxb_css_selector_list_selectors_remove(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *list) +{ + lxb_css_selector_list_remove(list); + + if (selectors->list == list) { + selectors->list = list->next; + } + + if (selectors->list_last == list) { + selectors->list_last = list->prev; + } +} + +void +lxb_css_selector_list_destroy(lxb_css_selector_list_t *list) +{ + if (list != NULL) { + lxb_css_selector_destroy_chain(list->first); + lexbor_dobject_free(list->memory->objs, list); + } +} + +void +lxb_css_selector_list_destroy_chain(lxb_css_selector_list_t *list) +{ + lxb_css_selector_list_t *next; + + while (list != NULL) { + next = list->next; + lxb_css_selector_list_destroy(list); + list = next; + } +} + +void +lxb_css_selector_list_destroy_memory(lxb_css_selector_list_t *list) +{ + if (list != NULL) { + (void) lxb_css_memory_destroy(list->memory, true); + } +} + +static void +lxb_css_selector_destroy_undef(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + /* Do nothing. */ +} + +static void +lxb_css_selector_destroy_any(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->ns.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->ns.data); + } + + if (selector->name.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->name.data); + } +} + +static void +lxb_css_selector_destroy_id(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->name.data != NULL) { + (void) lexbor_mraw_free(mem->mraw, selector->name.data); + } +} + +static void +lxb_css_selector_destroy_attribute(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->ns.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->ns.data); + } + + if (selector->name.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->name.data); + } + + if (selector->u.attribute.value.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->u.attribute.value.data); + } +} + +static void +lxb_css_selector_destroy_pseudo(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + if (selector->name.data != NULL) { + lexbor_mraw_free(mem->mraw, selector->name.data); + } +} + +static void +lxb_css_selector_destroy_pseudo_class_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + lxb_css_selector_anb_of_t *anbof; + lxb_css_selector_contains_t *contains; + lxb_css_selector_pseudo_t *pseudo; + + pseudo = &selector->u.pseudo; + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + anbof = pseudo->data; + + if (anbof != NULL) { + lxb_css_selector_list_destroy_chain(anbof->of); + lexbor_mraw_free(mem->mraw, anbof); + } + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: + lxb_css_selector_list_destroy_chain(pseudo->data); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LEXBOR_CONTAINS: + contains = pseudo->data; + + if (contains != NULL) { + if (contains->str.data != NULL) { + lexbor_mraw_free(mem->mraw, contains->str.data); + } + + lexbor_mraw_free(mem->mraw, contains); + } + break; + + default: + break; + } + + lxb_css_selector_destroy_pseudo(selector, mem); +} + +static void +lxb_css_selector_destroy_pseudo_element_function(lxb_css_selector_t *selector, + lxb_css_memory_t *mem) +{ + lxb_css_selector_destroy_pseudo(selector, mem); +} + +lxb_status_t +lxb_css_selector_serialize(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_selector_serialize_map[selector->type](selector, cb, ctx); +} + +lxb_status_t +lxb_css_selector_serialize_chain(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t *data; + lxb_status_t status; + + if (selector == NULL) { + return LXB_STATUS_OK; + } + + if (selector->combinator > LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + data = lxb_css_selector_combinator(selector, &length); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_selector_serialize_write(data, length); + lxb_css_selector_serialize_write(" ", 1); + } + + status = lxb_css_selector_serialize(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + selector = selector->next; + + while (selector != NULL) { + data = lxb_css_selector_combinator(selector, &length); + if (data == NULL) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (length != 0) { + lxb_css_selector_serialize_write(" ", 1); + + if (*data != ' ') { + lxb_css_selector_serialize_write(data, length); + lxb_css_selector_serialize_write(" ", 1); + } + } + + status = lxb_css_selector_serialize(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + selector = selector->next; + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_chain_char(lxb_css_selector_t *selector, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_chain(selector, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_chain(selector, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_selector_serialize_list(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + if (list != NULL) { + return lxb_css_selector_serialize_chain(list->first, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_list_char(lxb_css_selector_list_t *list, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_status_t +lxb_css_selector_serialize_list_chain(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + if (list == NULL) { + return LXB_STATUS_OK; + } + + status = lxb_css_selector_serialize_chain(list->first, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + list = list->next; + + while (list != NULL) { + lxb_css_selector_serialize_write(", ", 2); + + status = lxb_css_selector_serialize_chain(list->first, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + list = list->next; + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_serialize_list_chain_char(lxb_css_selector_list_t *list, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_selector_serialize_list_chain(list, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + + +static lxb_status_t +lxb_css_selector_serialize_undef(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return LXB_STATUS_ERROR_UNEXPECTED_DATA; +} + +static lxb_status_t +lxb_css_selector_serialize_any(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + if (selector->ns.data != NULL) { + lxb_css_selector_serialize_write(selector->ns.data, + selector->ns.length); + lxb_css_selector_serialize_write("|", 1); + } + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_id(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + lxb_css_selector_serialize_write("#", 1); + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + lxb_css_selector_serialize_write(".", 1); + + if (selector->name.data != NULL) { + return cb(selector->name.data, selector->name.length, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_escape_write(lxb_char_t *p, lxb_char_t *end, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t *begin; + lxb_status_t status; + + begin = p; + + lxb_css_selector_serialize_write("\"", 1); + + while (p < end) { + if (*p == '"') { + if (begin < p) { + lxb_css_selector_serialize_write(begin, p - begin); + } + + lxb_css_selector_serialize_write("\\000022", 7); + + begin = p + 1; + } + + p++; + } + + if (begin < p) { + lxb_css_selector_serialize_write(begin, p - begin); + } + + lxb_css_selector_serialize_write("\"", 1); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_attribute(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t *p, *end; + lxb_status_t status; + lxb_css_selector_attribute_t *attr; + + lxb_css_selector_serialize_write("[", 1); + + status = lxb_css_selector_serialize_any(selector, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + attr = &selector->u.attribute; + + if (attr->value.data == NULL) { + return cb((lxb_char_t *) "]", 1, ctx); + } + + switch (attr->match) { + case LXB_CSS_SELECTOR_MATCH_EQUAL: + lxb_css_selector_serialize_write("=", 1); + break; + case LXB_CSS_SELECTOR_MATCH_INCLUDE: + lxb_css_selector_serialize_write("~=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_DASH: + lxb_css_selector_serialize_write("|=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_PREFIX: + lxb_css_selector_serialize_write("^=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_SUFFIX: + lxb_css_selector_serialize_write("$=", 2); + break; + case LXB_CSS_SELECTOR_MATCH_SUBSTRING: + lxb_css_selector_serialize_write("*=", 2); + break; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + p = attr->value.data; + end = attr->value.data + attr->value.length; + + status = lxb_css_selector_serialize_escape_write(p, end, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (attr->modifier != LXB_CSS_SELECTOR_MODIFIER_UNSET) { + switch (attr->modifier) { + case LXB_CSS_SELECTOR_MODIFIER_I: + lxb_css_selector_serialize_write("i", 1); + break; + + case LXB_CSS_SELECTOR_MODIFIER_S: + lxb_css_selector_serialize_write("s", 1); + break; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + } + + return cb((lxb_char_t *) "]", 1, ctx); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_class(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_selector_serialize_pseudo_single(selector, cb, ctx, true); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_class_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + lxb_char_t *p, *end; + lxb_css_selector_pseudo_t *pseudo; + lxb_css_selector_contains_t *contains; + const lxb_css_selectors_pseudo_data_func_t *pfunc; + + pseudo = &selector->u.pseudo; + + pfunc = &lxb_css_selectors_pseudo_data_pseudo_class_function[pseudo->type]; + + lxb_css_selector_serialize_write(":", 1); + lxb_css_selector_serialize_write(pfunc->name, pfunc->length); + lxb_css_selector_serialize_write("(", 1); + + switch (pseudo->type) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_CURRENT: + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_HAS: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_IS: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NOT: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_OF_TYPE: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_OF_TYPE: + status = LXB_STATUS_OK; + + if (pseudo->data != NULL) { + status = lxb_css_selector_serialize_anb_of(pseudo->data, + cb, ctx); + } + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE: + status = lxb_css_selector_serialize_list_chain(pseudo->data, + cb, ctx); + break; + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LEXBOR_CONTAINS: + contains = pseudo->data; + p = contains->str.data; + end = p + contains->str.length; + + status = lxb_css_selector_serialize_escape_write(p, end, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (contains->insensitive) { + lxb_css_selector_serialize_write(" i", 2); + } + + break; + + default: + status = LXB_STATUS_OK; + break; + } + + if (status != LXB_STATUS_OK) { + return status; + } + + lxb_css_selector_serialize_write(")", 1); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_element(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return lxb_css_selector_serialize_pseudo_single(selector, cb, ctx, false); +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_element_function(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx) +{ + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selector_serialize_pseudo_single(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx, + bool is_class) +{ + lxb_status_t status; + lxb_css_selector_pseudo_t *pseudo; + const lxb_css_selectors_pseudo_data_t *pclass; + + pseudo = &selector->u.pseudo; + + if (is_class) { + pclass = &lxb_css_selectors_pseudo_data_pseudo_class[pseudo->type]; + lxb_css_selector_serialize_write(":", 1); + } + else { + pclass = &lxb_css_selectors_pseudo_data_pseudo_element[pseudo->type]; + lxb_css_selector_serialize_write("::", 2); + } + + lxb_css_selector_serialize_write(pclass->name, pclass->length); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_selector_serialize_anb_of(lxb_css_selector_anb_of_t *anbof, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + + static const lxb_char_t of[] = " of "; + + status = lxb_css_syntax_anb_serialize(&anbof->anb, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (anbof->of != NULL) { + lxb_css_selector_serialize_write(of, sizeof(of) - 1); + + return lxb_css_selector_serialize_list_chain(anbof->of, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_char_t * +lxb_css_selector_combinator(lxb_css_selector_t *selector, size_t *out_length) +{ + switch (selector->combinator) { + case LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) " "; + + case LXB_CSS_SELECTOR_COMBINATOR_CLOSE: + if (out_length != NULL) {*out_length = 0;} + return (lxb_char_t *) ""; + + case LXB_CSS_SELECTOR_COMBINATOR_CHILD: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) ">"; + + case LXB_CSS_SELECTOR_COMBINATOR_SIBLING: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) "+"; + + case LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING: + if (out_length != NULL) {*out_length = 1;} + return (lxb_char_t *) "~"; + + case LXB_CSS_SELECTOR_COMBINATOR_CELL: + if (out_length != NULL) {*out_length = 2;} + return (lxb_char_t *) "||"; + + default: + if (out_length != NULL) {*out_length = 0;} + return NULL; + } +} + +void +lxb_css_selector_list_append(lxb_css_selector_list_t *list, + lxb_css_selector_t *selector) +{ + selector->prev = list->last; + + if (list->last != NULL) { + list->last->next = selector; + } + else { + list->first = selector; + } + + list->last = selector; +} + +void +lxb_css_selector_append_next(lxb_css_selector_t *dist, lxb_css_selector_t *src) +{ + if (dist->next != NULL) { + dist->next->prev = src; + } + + src->prev = dist; + src->next = dist->next; + + dist->next = src; +} + +void +lxb_css_selector_list_append_next(lxb_css_selector_list_t *dist, + lxb_css_selector_list_t *src) +{ + if (dist->next != NULL) { + dist->next->prev = src; + } + + src->prev = dist; + src->next = dist->next; + + dist->next = src; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.h new file mode 100644 index 0000000..a5d373f --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selector.h @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_SELECTOR_H +#define LEXBOR_CSS_SELECTORS_SELECTOR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/str.h" +#include "lexbor/css/selectors/base.h" +#include "lexbor/css/syntax/anb.h" + + +typedef enum { + LXB_CSS_SELECTOR_TYPE__UNDEF = 0x00, + LXB_CSS_SELECTOR_TYPE_ANY, + LXB_CSS_SELECTOR_TYPE_ELEMENT, /* div, tag name
*/ + LXB_CSS_SELECTOR_TYPE_ID, /* #hash */ + LXB_CSS_SELECTOR_TYPE_CLASS, /* .class */ + LXB_CSS_SELECTOR_TYPE_ATTRIBUTE, /* [key=val], <... key="val"> */ + LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS, /* :pseudo */ + LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION, /* :function(...) */ + LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT, /* ::pseudo */ + LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION, /* ::function(...) */ + LXB_CSS_SELECTOR_TYPE__LAST_ENTRY +} +lxb_css_selector_type_t; + +typedef enum { + LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT = 0x00, /* WHITESPACE */ + LXB_CSS_SELECTOR_COMBINATOR_CLOSE, /* two compound selectors [key=val].foo */ + LXB_CSS_SELECTOR_COMBINATOR_CHILD, /* '>' */ + LXB_CSS_SELECTOR_COMBINATOR_SIBLING, /* '+' */ + LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING, /* '~' */ + LXB_CSS_SELECTOR_COMBINATOR_CELL, /* '||' */ + LXB_CSS_SELECTOR_COMBINATOR__LAST_ENTRY +} +lxb_css_selector_combinator_t; + +typedef enum { + LXB_CSS_SELECTOR_MATCH_EQUAL = 0x00, /* = */ + LXB_CSS_SELECTOR_MATCH_INCLUDE, /* ~= */ + LXB_CSS_SELECTOR_MATCH_DASH, /* |= */ + LXB_CSS_SELECTOR_MATCH_PREFIX, /* ^= */ + LXB_CSS_SELECTOR_MATCH_SUFFIX, /* $= */ + LXB_CSS_SELECTOR_MATCH_SUBSTRING, /* *= */ + LXB_CSS_SELECTOR_MATCH__LAST_ENTRY +} +lxb_css_selector_match_t; + +typedef enum { + LXB_CSS_SELECTOR_MODIFIER_UNSET = 0x00, + LXB_CSS_SELECTOR_MODIFIER_I, + LXB_CSS_SELECTOR_MODIFIER_S, + LXB_CSS_SELECTOR_MODIFIER__LAST_ENTRY +} +lxb_css_selector_modifier_t; + +typedef struct { + lxb_css_selector_match_t match; + lxb_css_selector_modifier_t modifier; + lexbor_str_t value; +} +lxb_css_selector_attribute_t; + +typedef struct { + unsigned type; + void *data; +} +lxb_css_selector_pseudo_t; + +typedef struct { + lxb_css_syntax_anb_t anb; + lxb_css_selector_list_t *of; +} +lxb_css_selector_anb_of_t; + +typedef struct { + lexbor_str_t str; + bool insensitive; +} +lxb_css_selector_contains_t; + +struct lxb_css_selector { + lxb_css_selector_type_t type; + lxb_css_selector_combinator_t combinator; + + lexbor_str_t name; + lexbor_str_t ns; + + union lxb_css_selector_u { + lxb_css_selector_attribute_t attribute; + lxb_css_selector_pseudo_t pseudo; + } + u; + + lxb_css_selector_t *next; + lxb_css_selector_t *prev; + + lxb_css_selector_list_t *list; +}; + + +/* + * I S A B C + * 1 bit | 1 bit | 9 bit | 9 bit | 9 bit + */ +typedef uint32_t lxb_css_selector_specificity_t; + +#define LXB_CSS_SELECTOR_SPECIFICITY_MASK_N(sp, n) \ + ((sp) & ~((((uint32_t) 1 << 9) - 1) << (n))) + +#define LXB_CSS_SELECTOR_SPECIFICITY_MASK \ + ((((uint32_t) 1 << (32 - 9)) - 1) << (9)) + +/* + * CSS Selector Specificity field accessors. + * + * Specificity is packed into a single uint32_t: + * bits [31..28] — i: !important flag (1 = declaration is !important) + * bit [27] — s: style attribute flag (1 = from element's style="...") + * bits [26..18] — a: count of ID selectors + * bits [17..9] — b: count of class selectors, attribute selectors, + * and pseudo-classes + * bits [8..0] — c: count of type selectors and pseudo-elements + * + * Per CSS cascade order: !important > style attribute > (a, b, c). + */ + +/* Extract the !important flag (bits 31..28). */ +#define lxb_css_selector_sp_i(sp) ((sp) >> 28) + +/* Extract the style attribute flag (bit 27). */ +#define lxb_css_selector_sp_s(sp) \ + (((sp) >> 27) & ~((((uint32_t) 1 << 31) - 1) << (1))) + +/* Extract the ID selector count — component "a" (bits 26..18). */ +#define lxb_css_selector_sp_a(sp) \ + (((sp) >> 18) & ~LXB_CSS_SELECTOR_SPECIFICITY_MASK) + +/* Extract the class/attribute/pseudo-class count — component "b" (bits 17..9). */ +#define lxb_css_selector_sp_b(sp) \ + (((sp) >> 9) & ~LXB_CSS_SELECTOR_SPECIFICITY_MASK) + +/* Extract the type/pseudo-element count — component "c" (bits 8..0). */ +#define lxb_css_selector_sp_c(sp) \ + ((sp) & ~LXB_CSS_SELECTOR_SPECIFICITY_MASK) + +#define lxb_css_selector_sp_set_i(sp, num) \ + sp = (LXB_CSS_SELECTOR_SPECIFICITY_MASK_N((sp), 28) | ((num) << 28)) + +#define lxb_css_selector_sp_set_s(sp, num) \ + sp = (LXB_CSS_SELECTOR_SPECIFICITY_MASK_N((sp), 27) | ((num) << 27)) + +#define lxb_css_selector_sp_set_a(sp, num) \ + sp = (LXB_CSS_SELECTOR_SPECIFICITY_MASK_N((sp), 18) | ((num) << 18)) + +#define lxb_css_selector_sp_set_b(sp, num) \ + sp = (LXB_CSS_SELECTOR_SPECIFICITY_MASK_N((sp), 9) | ((num) << 9)) + +#define lxb_css_selector_sp_set_c(sp, num) \ + sp = (LXB_CSS_SELECTOR_SPECIFICITY_MASK_N((sp), 0) | (num)) + +#define lxb_css_selector_sp_add_s(sp, num) \ + (lxb_css_selector_sp_set_s((sp), lxb_css_selector_sp_s(sp) + num)) + +#define lxb_css_selector_sp_add_a(sp, num) \ + (lxb_css_selector_sp_set_a((sp), lxb_css_selector_sp_a(sp) + num)) + +#define lxb_css_selector_sp_add_b(sp, num) \ + (lxb_css_selector_sp_set_b((sp), lxb_css_selector_sp_b(sp) + num)) + +#define lxb_css_selector_sp_add_c(sp, num) \ + (lxb_css_selector_sp_set_c((sp), lxb_css_selector_sp_c(sp) + num)) + +#define lxb_css_selector_sp_up_i(num) (num | (1 << 28)) +#define lxb_css_selector_sp_up_s(num) (num | (1 << 27)) + +#define LXB_CSS_SELECTOR_SP_S_MAX ((1 << 28) - 1) +#define LXB_CSS_SELECTOR_SP_A_MAX ((1 << 27) - 1) +#define LXB_CSS_SELECTOR_SP_B_MAX ((1 << 18) - 1) +#define LXB_CSS_SELECTOR_SP_C_MAX ((1 << 9) - 1) + + +struct lxb_css_selector_list { + lxb_css_selector_t *first; + lxb_css_selector_t *last; + + lxb_css_selector_t *parent; + + lxb_css_selector_list_t *next; + lxb_css_selector_list_t *prev; + + lxb_css_memory_t *memory; + + lxb_css_selector_specificity_t specificity; +}; + + +LXB_API lxb_css_selector_t * +lxb_css_selector_create(lxb_css_selector_list_t *list); + +LXB_API void +lxb_css_selector_destroy(lxb_css_selector_t *selector); + +LXB_API void +lxb_css_selector_destroy_chain(lxb_css_selector_t *selector); + +LXB_API void +lxb_css_selector_remove(lxb_css_selector_t *selector); + +LXB_API lxb_css_selector_list_t * +lxb_css_selector_list_create(lxb_css_memory_t *mem); + +LXB_API void +lxb_css_selector_list_remove(lxb_css_selector_list_t *list); + +LXB_API void +lxb_css_selector_list_selectors_remove(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *list); + +LXB_API void +lxb_css_selector_list_destroy(lxb_css_selector_list_t *list); + +LXB_API void +lxb_css_selector_list_destroy_chain(lxb_css_selector_list_t *list); + +LXB_API void +lxb_css_selector_list_destroy_memory(lxb_css_selector_list_t *list); + +LXB_API lxb_status_t +lxb_css_selector_serialize(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_selector_serialize_chain(lxb_css_selector_t *selector, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_char_t * +lxb_css_selector_serialize_chain_char(lxb_css_selector_t *selector, + size_t *out_length); + +LXB_API lxb_status_t +lxb_css_selector_serialize_list(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_selector_serialize_list_chain(lxb_css_selector_list_t *list, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_char_t * +lxb_css_selector_serialize_list_chain_char(lxb_css_selector_list_t *list, + size_t *out_length); + +LXB_API lxb_status_t +lxb_css_selector_serialize_anb_of(lxb_css_selector_anb_of_t *anbof, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_char_t * +lxb_css_selector_combinator(lxb_css_selector_t *selector, size_t *out_length); + +LXB_API void +lxb_css_selector_list_append(lxb_css_selector_list_t *list, + lxb_css_selector_t *selector); + +LXB_API void +lxb_css_selector_list_append_next(lxb_css_selector_list_t *dist, + lxb_css_selector_list_t *src); + +LXB_API void +lxb_css_selector_append_next(lxb_css_selector_t *dist, lxb_css_selector_t *src); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_SELECTOR_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.c new file mode 100644 index 0000000..77586f4 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.c @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/print.h" +#include "lexbor/css/css.h" + + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_list(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length); + +static lxb_status_t +lxb_css_selectors_components_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_complex_list_cb = { + .prelude = lxb_css_selectors_state_complex_list, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_compound_list_cb = { + .prelude = lxb_css_selectors_state_compound_list, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_simple_list_cb = { + .prelude = lxb_css_selectors_state_simple_list, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_relative_list_cb = { + .prelude = lxb_css_selectors_state_relative_list, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_complex_cb = { + .prelude = lxb_css_selectors_state_complex, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_compound_cb = { + .prelude = lxb_css_selectors_state_compound, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_simple_cb = { + .prelude = lxb_css_selectors_state_simple, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + +static const lxb_css_syntax_cb_components_t lxb_css_selectors_relative_cb = { + .prelude = lxb_css_selectors_state_relative, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_selectors_components_end +}; + + +lxb_css_selectors_t * +lxb_css_selectors_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_selectors_t)); +} + +lxb_status_t +lxb_css_selectors_init(lxb_css_selectors_t *selectors) +{ + if (selectors == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + selectors->list = NULL; + selectors->list_last = NULL; + selectors->parent = NULL; + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->error = 0; + selectors->err_in_function = false; + selectors->failed = false; + + return LXB_STATUS_OK; +} + +void +lxb_css_selectors_clean(lxb_css_selectors_t *selectors) +{ + if (selectors != NULL) { + selectors->list = NULL; + selectors->list_last = NULL; + selectors->parent = NULL; + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + selectors->error = 0; + selectors->err_in_function = false; + selectors->failed = false; + } +} + +lxb_css_selectors_t * +lxb_css_selectors_destroy(lxb_css_selectors_t *selectors, bool self_destroy) +{ + if (selectors == NULL) { + return NULL; + } + + if (self_destroy) { + return lexbor_free(selectors); + } + + return selectors; +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_complex_list(parser, data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_complex_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_complex_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_compound_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_compound_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_simple_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_simple_list_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_relative_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_relative_list_cb, + data, length); +} + +static lxb_status_t +lxb_css_selectors_parse_prepare(lxb_css_parser_t *parser) +{ + if (parser->stage != LXB_CSS_PARSER_CLEAN) { + if (parser->stage == LXB_CSS_PARSER_RUN) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + lxb_css_parser_clean(parser); + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + return LXB_STATUS_OK; +} + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_process(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length) +{ + lxb_css_syntax_rule_t *rule; + + lxb_css_parser_buffer_set(parser, data, length); + + rule = lxb_css_syntax_parser_components_push(parser, components, NULL, NULL, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + return NULL; + } + + parser->status = lxb_css_syntax_parser_run(parser); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + + return parser->selectors->list; +} + +static void +lxb_css_selectors_parse_finish(lxb_css_parser_t *parser) +{ + parser->stage = LXB_CSS_PARSER_END; +} + +static lxb_css_selector_list_t * +lxb_css_selectors_parse_list(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *components, + const lxb_char_t *data, size_t length) +{ + bool my_mem, my_selectors; + lxb_css_selector_list_t *list; + + my_mem = false; + my_selectors = false; + + if (parser->selectors == NULL) { + parser->selectors = lxb_css_selectors_create(); + parser->status = lxb_css_selectors_init(parser->selectors); + + if (parser->status != LXB_STATUS_OK) { + (void) lxb_css_selectors_destroy(parser->selectors, true); + return NULL; + } + + my_selectors = true; + } + else { + lxb_css_selectors_clean(parser->selectors); + } + + if (parser->memory == NULL) { + parser->memory = lxb_css_memory_create(); + parser->status = lxb_css_memory_init(parser->memory, 256); + + if (parser->status != LXB_STATUS_OK) { + if (my_selectors) { + parser->selectors = lxb_css_selectors_destroy(parser->selectors, + true); + } + + (void) lxb_css_memory_destroy(parser->memory, true); + return NULL; + } + + my_mem = true; + } + + parser->status = lxb_css_selectors_parse_prepare(parser); + if (parser->status != LXB_STATUS_OK) { + list = NULL; + goto end; + } + + list = lxb_css_selectors_parse_process(parser, components, data, length); + + lxb_css_selectors_parse_finish(parser); + +end: + + if (list == NULL && my_mem) { + parser->memory = lxb_css_memory_destroy(parser->memory, true); + } + + if (my_selectors) { + parser->selectors = lxb_css_selectors_destroy(parser->selectors, true); + } + + return list; +} + +static lxb_status_t +lxb_css_selectors_components_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_selector_list_t *list; + lxb_css_selectors_t *selectors = parser->selectors; + + if (failed) { + list = selectors->list_last; + + if (list != NULL) { + lxb_css_selector_list_selectors_remove(selectors, list); + lxb_css_selector_list_destroy(list); + } + } + + return LXB_STATUS_OK; +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_complex(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_complex_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_compound(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_compound_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_simple(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_simple_cb, + data, length); +} + +lxb_css_selector_list_t * +lxb_css_selectors_parse_relative(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + return lxb_css_selectors_parse_list(parser, &lxb_css_selectors_relative_cb, + data, length); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.h new file mode 100644 index 0000000..2adfecc --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/selectors.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2020-2022 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_H +#define LEXBOR_CSS_SELECTORS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/dobject.h" +#include "lexbor/css/base.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/selectors/base.h" +#include "lexbor/css/selectors/selector.h" +#include "lexbor/css/selectors/pseudo_const.h" + + +struct lxb_css_selectors { + lxb_css_selector_list_t *list; + lxb_css_selector_list_t *list_last; + + lxb_css_selector_t *parent; + + lxb_css_selector_combinator_t combinator; + lxb_css_selector_combinator_t comb_default; + + uintptr_t error; + bool status; + bool err_in_function; + bool failed; +}; + + +LXB_API lxb_css_selectors_t * +lxb_css_selectors_create(void); + +LXB_API lxb_status_t +lxb_css_selectors_init(lxb_css_selectors_t *selectors); + +LXB_API void +lxb_css_selectors_clean(lxb_css_selectors_t *selectors); + +LXB_API lxb_css_selectors_t * +lxb_css_selectors_destroy(lxb_css_selectors_t *selectors, bool self_destroy); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_complex_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_compound_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_simple_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_relative_list(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_complex(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_compound(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_simple(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_selector_list_t * +lxb_css_selectors_parse_relative(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +/* + * Inline functions + */ +lxb_inline void +lxb_css_selectors_append_next(lxb_css_selectors_t *selectors, + lxb_css_selector_t *selector) +{ + if (selectors->list_last->last != NULL) { + lxb_css_selector_append_next(selectors->list_last->last, selector); + } + else { + selectors->list_last->first = selector; + } + + selectors->list_last->last = selector; +} + +lxb_inline void +lxb_css_selectors_list_append_next(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *list) +{ + if (selectors->list_last != NULL) { + lxb_css_selector_list_append_next(selectors->list_last, list); + } + else { + selectors->list = list; + } + + selectors->list_last = list; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.c new file mode 100644 index 0000000..3ebdfbe --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.c @@ -0,0 +1,2060 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/parser.h" +#include "lexbor/css/css.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/pseudo.h" +#include "lexbor/css/selectors/pseudo_const.h" + + +static const char lxb_css_selectors_module_name[] = "Selectors"; + + +#define lxb_css_selectors_state_string_dup_m(selectors, name) \ + do { \ + (status) = lxb_css_syntax_token_string_dup( \ + lxb_css_syntax_token_string(token), (name), \ + (parser)->memory->mraw); \ + if ((status) != LXB_STATUS_OK) { \ + return (status); \ + } \ + } \ + while (false) + +#define lxb_css_selectors_state_append(parser, selectors, selector) \ + do { \ + (selector) = lxb_css_selector_create((selectors)->list_last); \ + if ((selector) == NULL) { \ + return lxb_css_parser_memory_fail(parser); \ + } \ + \ + lxb_css_selectors_append_next((selectors), (selector)); \ + \ + (selector)->combinator = (selectors)->combinator; \ + (selectors)->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; \ + } \ + while (false) + +#define lxb_css_selectors_state_list_append(parser, selectors, list) \ + do { \ + (list) = lxb_css_selector_list_create((parser)->memory); \ + if ((list) == NULL) { \ + return lxb_css_parser_memory_fail(parser); \ + } \ + \ + lxb_css_selectors_list_append_next((selectors), (list)); \ + \ + (list)->parent = selectors->parent; \ + } \ + while (false) + + +static bool +lxb_css_selectors_state_complex_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_list_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_relative_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool list, bool root); + +static bool +lxb_css_selectors_state_complex_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_complex_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_complex_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool root); + +static bool +lxb_css_selectors_state_compound_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_sub(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_compound_pseudo(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static bool +lxb_css_selectors_state_simple_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_selectors_state_hash(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_element_ns(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_attribute(lxb_css_parser_t *parser); + +static lxb_status_t +lxb_css_selectors_state_ns(lxb_css_parser_t *parser, + lxb_css_selector_t *selector); + +static lxb_status_t +lxb_css_selectors_state_ns_ident(lxb_css_parser_t *parser, + lxb_css_selector_t *selector); + +static lxb_status_t +lxb_css_selectors_state_pseudo_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_pseudo_class_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success); + +static lxb_status_t +lxb_css_selectors_state_pseudo_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_selectors_state_pseudo_element_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success); + +static lxb_status_t +lxb_css_selectors_state_forgiving_cb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, lxb_css_parser_state_f state, + bool failed); + +static void +lxb_css_selectors_state_restore_parent(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *last); + +static bool +lxb_css_selectors_state_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state); + +static bool +lxb_css_selectors_state_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static const lxb_css_syntax_token_t * +lxb_css_selectors_state_function_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + + +lxb_inline bool +lxb_css_selectors_done(lxb_css_parser_t *parser) +{ + lxb_css_parser_states_pop(parser); + + return lxb_css_parser_states_set_back(parser); +} + +lxb_inline void +lxb_css_selectors_state_specificity_set_b(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_b(last->specificity, + lxb_css_selector_sp_b(last->specificity) + 1); + } + else if (last->specificity > LXB_CSS_SELECTOR_SP_B_MAX) { + if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + lxb_css_selector_sp_set_b(last->specificity, 1); + } + } + else { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_b(last->specificity, 1); + } +} + +lxb_inline void +lxb_css_selectors_state_specificity_set_c(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_c(last->specificity, + lxb_css_selector_sp_c(last->specificity) + 1); + } + else if (last->specificity > LXB_CSS_SELECTOR_SP_C_MAX) { + if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + lxb_css_selector_sp_set_c(last->specificity, 1); + } + } + else { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_c(last->specificity, 1); + } +} + +lxb_inline void +lxb_css_selectors_state_func_specificity(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_list_t *prev, *last; + + last = selectors->list_last; + prev = last->prev; + + if (prev->specificity > last->specificity) { + last->specificity = prev->specificity; + } + + prev->specificity = 0; +} + +/* + * + */ +bool +lxb_css_selectors_state_complex_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_complex_wo_root, + lxb_css_selectors_state_complex_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_complex_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_relative_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_relative_list_wo_root, + lxb_css_selectors_state_relative_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_relative_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_relative_list_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_relative_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_relative_list_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_relative_handler(parser, token, ctx, true, + false); +} + +static bool +lxb_css_selectors_state_relative_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_relative_handler(parser, token, ctx, false, + false); +} + +static bool +lxb_css_selectors_state_relative_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool list, bool root) +{ + lxb_css_parser_state_f back; + lxb_css_parser_state_t *states; + lxb_css_selectors_t *selectors = parser->selectors; + + /* */ + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + return true; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '>': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CHILD; + break; + + case '+': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_SIBLING; + break; + + case '~': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING; + break; + + case '|': + lxb_css_parser_token_next_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CELL; + break; + } + + goto done; + + default: + goto done; + } + + break; + + default: + goto done; + } + + lxb_css_syntax_parser_consume(parser); + +done: + + back = (list) ? lxb_css_selectors_state_complex_end + : lxb_css_selectors_state_end; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + back, ctx, root); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return true; +} + +/* + * + */ +bool +lxb_css_selectors_state_complex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_complex_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_complex_handler(parser, token, ctx, false); +} + +static bool +lxb_css_selectors_state_complex_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool root) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_complex_end, + ctx, root); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_complex_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selectors_t *selectors = parser->selectors; + + /* */ + +again: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + return lxb_css_selectors_done(parser); + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '>': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CHILD; + break; + + case '+': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_SIBLING; + break; + + case '~': + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_FOLLOWING; + break; + + case '|': + lxb_css_parser_token_next_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CELL; + break; + } + + goto done; + + default: + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT) { + goto unexpected; + } + + goto done; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return lxb_css_selectors_done(parser); + + default: + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT) { + goto unexpected; + } + + goto done; + } + + lxb_css_syntax_parser_consume(parser); + +done: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_handler); + + return true; + +unexpected: + + (void) lxb_css_selectors_done(parser); + + return lxb_css_parser_unexpected(parser); +} + +/* + * + */ +bool +lxb_css_selectors_state_compound_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_compound_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_compound_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_compound_wo_root); +} + +/* + * + * + */ +bool +lxb_css_selectors_state_compound(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_compound_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_compound_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selector_list_t *list; + + lxb_css_selectors_state_list_append(parser, parser->selectors, list); + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_handler); + + return false; +} + +static bool +lxb_css_selectors_state_compound_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + lxb_css_selectors_t *selectors; + +again: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_compound_sub); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + case '|': + case '*': + status = lxb_css_selectors_state_element_ns(parser, token); + break; + + default: + goto unexpected; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + status = lxb_css_selectors_state_element(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_compound_pseudo); + status = lxb_css_selectors_state_pseudo_element(parser, token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_element_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + goto unexpected; + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_sub); + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + selectors = parser->selectors; + + if (selectors->combinator > LXB_CSS_SELECTOR_COMBINATOR_CLOSE + || selectors->list_last->first == NULL) + { + goto unexpected; + } + + return lxb_css_selectors_done(parser); + + default: + goto unexpected; + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + +unexpected: + + (void) lxb_css_parser_states_to_root(parser); + (void) lxb_css_parser_states_set_back(parser); + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_compound_sub(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + + /* */ + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + default: + return lxb_css_parser_states_set_back(parser); + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + lxb_css_parser_state_set(parser, + lxb_css_selectors_state_compound_pseudo); + status = lxb_css_selectors_state_pseudo_element(parser, + token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_element_function(parser, + token, lxb_css_selectors_state_compound_pseudo); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_sub); + break; + + default: + return lxb_css_parser_states_set_back(parser); + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_compound_pseudo(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COLON) { + return lxb_css_parser_states_set_back(parser); + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_element(parser, token); + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + status = lxb_css_selectors_state_pseudo_element_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + } + else { + return lxb_css_parser_unexpected(parser); + } + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return lxb_css_parser_unexpected(parser); + } + else { + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_compound_pseudo); + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + + return lxb_css_parser_unexpected(parser); +} + +/* + * + */ +bool +lxb_css_selectors_state_simple_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, lxb_css_selectors_state_simple_wo_root, + lxb_css_selectors_state_simple_list_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_simple_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_selectors_state_list_end(parser, token, + lxb_css_selectors_state_simple_wo_root); +} + +/* + * + */ +bool +lxb_css_selectors_state_simple(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + states = lxb_css_parser_states_next(parser, + lxb_css_selectors_state_simple_wo_root, + lxb_css_selectors_state_end, + ctx, true); + if (states == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + return false; +} + +static bool +lxb_css_selectors_state_simple_wo_root(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_selector_list_t *list; + + lxb_css_selectors_state_list_append(parser, parser->selectors, list); + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_simple_handler); + + return false; +} + +static bool +lxb_css_selectors_state_simple_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_status_t status; + +again: + + lxb_css_parser_state_set(parser, lxb_css_selectors_state_simple_back); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = lxb_css_selectors_state_hash(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '.': + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_class(parser, token); + break; + + case '|': + case '*': + status = lxb_css_selectors_state_element_ns(parser, token); + break; + + default: + goto unexpected; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + status = lxb_css_selectors_state_element(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + lxb_css_syntax_parser_consume(parser); + status = lxb_css_selectors_state_attribute(parser); + break; + + case LXB_CSS_SYNTAX_TOKEN_COLON: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + status = lxb_css_selectors_state_pseudo_class(parser, token); + break; + } + else if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + goto unexpected; + } + + status = lxb_css_selectors_state_pseudo_class_function(parser, token, + lxb_css_selectors_state_simple_back); + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_m(parser, token); + goto again; + + case LXB_CSS_SYNTAX_TOKEN__END: + return lxb_css_parser_states_set_back(parser); + + default: + goto unexpected; + } + + if (status == LXB_STATUS_OK) { + return true; + } + + if (status == LXB_STATUS_ERROR_MEMORY_ALLOCATION) { + return lxb_css_parser_memory_fail(parser); + } + +unexpected: + + (void) lxb_css_parser_states_set_back(parser); + + return lxb_css_parser_unexpected(parser); +} + +static bool +lxb_css_selectors_state_simple_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + return lxb_css_parser_states_set_back(parser); +} + +static lxb_status_t +lxb_css_selectors_state_hash(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_selector_list_t *last; + + selectors = parser->selectors; + last = selectors->list_last; + + if (selectors->parent == NULL) { + lxb_css_selector_sp_set_a(last->specificity, + lxb_css_selector_sp_a(last->specificity) + 1); + } + else if (lxb_css_selector_sp_a(last->specificity) == 0) { + if (selectors->combinator != LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + last->specificity = 0; + } + + lxb_css_selector_sp_set_a(last->specificity, 1); + } + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ID; + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + lxb_css_syntax_parser_consume(parser); + + return status; +} + +static lxb_status_t +lxb_css_selectors_state_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_b(selectors); + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_CLASS; + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + lxb_css_syntax_parser_consume(parser); + + return status; + } + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_element_ns(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ANY; + + selector->name.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->name.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->name.data[0] = '*'; + selector->name.data[1] = '\0'; + selector->name.length = 1; + + if (lxb_css_syntax_token_delim_char(token) == '*') { + lxb_css_syntax_parser_consume(parser); + return lxb_css_selectors_state_ns(parser, selector); + } + + selector->name.data[0] = '\0'; + selector->name.length = 0; + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_selectors_state_ns_ident(parser, selector); +} + +static lxb_status_t +lxb_css_selectors_state_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_c(selectors); + + lxb_css_selectors_state_append(parser, selectors, selector); + + selector->type = LXB_CSS_SELECTOR_TYPE_ELEMENT; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_selectors_state_ns(parser, selector); +} + + +static lxb_status_t +lxb_css_selectors_state_attribute(lxb_css_parser_t *parser) +{ + lxb_char_t modifier; + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_syntax_token_t *token; + lxb_css_selector_attribute_t *attribute; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim_char(token) != '|') { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + goto failed; + } + + selector->type = LXB_CSS_SELECTOR_TYPE_ATTRIBUTE; + + selector->ns.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->ns.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->ns.data[0] = '*'; + selector->ns.data[1] = '\0'; + selector->ns.length = 1; + + lxb_css_selectors_state_string_dup_m(parser->selectors, + &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + selector->type = LXB_CSS_SELECTOR_TYPE_ATTRIBUTE; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_DELIM + || lxb_css_syntax_token_delim_char(token) != '|') + { + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + break; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + attribute = &selector->u.attribute; + attribute->match = LXB_CSS_SELECTOR_MATCH_DASH; + + goto assignment; + } + + selector->ns = selector->name; + lexbor_str_clean_all(&selector->name); + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + break; + + default: + goto failed; + } + + attribute = &selector->u.attribute; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + goto done; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + switch (lxb_css_syntax_token_delim_char(token)) { + case '~': + attribute->match = LXB_CSS_SELECTOR_MATCH_INCLUDE; + break; + + case '|': + attribute->match = LXB_CSS_SELECTOR_MATCH_DASH; + break; + + case '^': + attribute->match = LXB_CSS_SELECTOR_MATCH_PREFIX; + break; + + case '$': + attribute->match = LXB_CSS_SELECTOR_MATCH_SUFFIX; + break; + + case '*': + attribute->match = LXB_CSS_SELECTOR_MATCH_SUBSTRING; + break; + + case '=': + attribute->match = LXB_CSS_SELECTOR_MATCH_EQUAL; + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + goto string_or_ident; + + default: + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + break; + + case LXB_CSS_SYNTAX_TOKEN__END: + goto done_eof; + + default: + goto failed; + } + +assignment: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_DELIM + || lxb_css_syntax_token_delim_char(token) != '=') + { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + +string_or_ident: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_STRING + && token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) + { + goto failed; + } + + lxb_css_selectors_state_string_dup_m(selectors, &attribute->value); + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + goto done; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + if (lxb_css_syntax_token_string(token)->length != 1) { + goto failed; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN__END: + goto done_eof; + + default: + goto failed; + } + + modifier = *lxb_css_syntax_token_string(token)->data; + + switch (modifier) { + case 'i': + case 'I': + attribute->modifier = LXB_CSS_SELECTOR_MODIFIER_I; + break; + + case 's': + case 'S': + attribute->modifier = LXB_CSS_SELECTOR_MODIFIER_S; + break; + + default: + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_RS_BRACKET) { + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + goto done_eof; + } + + goto failed; + } + +done: + + lxb_css_selectors_state_specificity_set_b(selectors); + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +done_eof: + + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, + "%s. End Of File in attribute selector", + lxb_css_selectors_module_name); + + lxb_css_selectors_state_specificity_set_b(selectors); + + return LXB_STATUS_OK; + +failed: + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_ns(lxb_css_parser_t *parser, + lxb_css_selector_t *selector) +{ + const lxb_css_syntax_token_t *token; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '|') + { + lxb_css_syntax_parser_consume(parser); + return lxb_css_selectors_state_ns_ident(parser, selector); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_ns_ident(lxb_css_parser_t *parser, + lxb_css_selector_t *selector) +{ + lxb_status_t status; + const lxb_css_syntax_token_t *token; + lxb_css_selectors_t *selectors; + + lxb_css_parser_token_status_m(parser, token); + + if (token->type == LXB_CSS_SYNTAX_TOKEN_IDENT) { + selectors = parser->selectors; + + lxb_css_selectors_state_specificity_set_c(selectors); + + selector->type = LXB_CSS_SELECTOR_TYPE_ELEMENT; + + selector->ns = selector->name; + lexbor_str_clean_all(&selector->name); + + status = lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string(token), + &selector->name, parser->memory->mraw); + + lxb_css_syntax_parser_consume(parser); + + return status; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM + && lxb_css_syntax_token_delim_char(token) == '*') + { + lxb_css_syntax_parser_consume(parser); + + selector->type = LXB_CSS_SELECTOR_TYPE_ANY; + + selector->ns = selector->name; + + selector->name.data = lexbor_mraw_alloc(parser->memory->mraw, 2); + if (selector->name.data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + selector->name.data[0] = '*'; + selector->name.data[1] = '\0'; + selector->name.length = 1; + + return LXB_STATUS_OK; + } + + return lxb_css_parser_unexpected_status(parser); +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_class(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_log_message_t *msg; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_selectors_pseudo_data_t *pseudo; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + pseudo = lxb_css_selector_pseudo_class_by_name(selector->name.data, + selector->name.length); + if (pseudo == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (pseudo->id) { + /* Bombshell note: lexbor rejects pseudo-classes its own matcher + * does not implement. Our runtime matcher handles them, so they + * are accepted at parse time instead of rejected. */ + default: + break; + } + + selector->u.pseudo.type = pseudo->id; + selector->u.pseudo.data = NULL; + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_class_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_log_message_t *msg; + lxb_css_syntax_rule_t *rule; + const lxb_css_selectors_pseudo_data_func_t *func; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + func = lxb_css_selector_pseudo_class_function_by_name(selector->name.data, + selector->name.length); + if (func == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (func->id) { + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_DIR: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_LANG: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_COL: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_COL: + msg = lxb_css_log_not_supported(parser->log, + lxb_css_selectors_module_name, + (const char *) selector->name.data); + if (msg == NULL) { + goto failed; + } + + return lxb_css_parser_unexpected_status(parser); + + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_CHILD: + case LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_NTH_LAST_CHILD: + lxb_css_selectors_state_specificity_set_b(selectors); + break; + + default: + break; + } + + selector->u.pseudo.type = func->id; + selector->u.pseudo.data = NULL; + + selectors->combinator = func->combinator; + selectors->comb_default = func->combinator; + selectors->parent = selector; + + rule = lxb_css_syntax_consume_function(parser, token, &func->cb, + success, selectors->list_last); + if (rule == NULL) { + goto failed; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +failed: + + (void) lxb_css_parser_memory_fail(parser); + + return parser->status; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_element(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_status_t status; + lxb_css_log_message_t *msg; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + const lxb_css_selectors_pseudo_data_t *pseudo; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + pseudo = lxb_css_selector_pseudo_element_by_name(selector->name.data, + selector->name.length); + if (pseudo == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + switch (pseudo->id) { + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_AFTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BACKDROP: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_BEFORE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LETTER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_FIRST_LINE: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_GRAMMAR_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_INACTIVE_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_MARKER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_PLACEHOLDER: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SELECTION: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_SPELLING_ERROR: + case LXB_CSS_SELECTOR_PSEUDO_ELEMENT_TARGET_TEXT: + msg = lxb_css_log_not_supported(parser->log, + lxb_css_selectors_module_name, + (const char *) selector->name.data); + if (msg == NULL) { + (void) lxb_css_parser_memory_fail(parser); + return parser->status; + } + + return lxb_css_parser_unexpected_status(parser); + + default: + break; + } + + selector->u.pseudo.type = pseudo->id; + selector->u.pseudo.data = NULL; + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_selectors_state_pseudo_element_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f success) +{ + lxb_status_t status; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors; + lxb_css_syntax_rule_t *rule; + const lxb_css_selectors_pseudo_data_func_t *func; + + selectors = parser->selectors; + + lxb_css_selectors_state_append(parser, selectors, selector); + selector->type = LXB_CSS_SELECTOR_TYPE_PSEUDO_ELEMENT_FUNCTION; + + lxb_css_selectors_state_string_dup_m(selectors, &selector->name); + + func = lxb_css_selector_pseudo_element_function_by_name(selector->name.data, + selector->name.length); + if (func == NULL) { + return lxb_css_parser_unexpected_status(parser); + } + + selector->u.pseudo.type = func->id; + selector->u.pseudo.data = NULL; + + selectors->combinator = func->combinator; + selectors->comb_default = func->combinator; + selectors->parent = selector; + + rule = lxb_css_syntax_consume_function(parser, token, &func->cb, + success, selectors->list_last); + if (rule == NULL) { + (void) lxb_css_parser_memory_fail(parser); + return parser->status; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +lxb_inline void +lxb_css_selectors_state_restore_combinator(lxb_css_selectors_t *selectors) +{ + lxb_css_selector_t *parent; + lxb_css_selector_combinator_t comb_default; + const lxb_css_selectors_pseudo_data_func_t *data_func; + + comb_default = LXB_CSS_SELECTOR_COMBINATOR_DESCENDANT; + + if (selectors->parent != NULL) { + parent = selectors->parent; + + if (parent->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION) { + data_func = lxb_css_selector_pseudo_class_function_by_id(parent->u.pseudo.type); + } + else { + data_func = lxb_css_selector_pseudo_element_function_by_id(parent->u.pseudo.type); + } + + comb_default = data_func->combinator; + } + + selectors->combinator = LXB_CSS_SELECTOR_COMBINATOR_CLOSE; + selectors->comb_default = comb_default; +} + +lxb_status_t +lxb_css_selectors_state_function_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + bool cy; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + + if (token->type == LXB_CSS_SYNTAX_TOKEN__EOF) { + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, + "%s. End Of File in pseudo function", + lxb_css_selectors_module_name); + } + + if (selectors->list_last == NULL) { + lxb_css_selectors_state_restore_parent(selectors, ctx); + goto empty; + } + + lxb_css_selectors_state_restore_parent(selectors, ctx); + + return LXB_STATUS_OK; + +empty: + + selector = selectors->list_last->last; + + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + cy = lxb_css_selector_pseudo_function_can_empty(selector->u.pseudo.type, + cy); + if (cy) { + lxb_css_parser_set_ok(parser); + return LXB_STATUS_OK; + } + + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, + "%s. Pseudo function can't be empty: %S()", + lxb_css_selectors_module_name, &selector->name); + + lxb_css_selector_remove(selector); + lxb_css_selector_destroy(selector); + + lxb_css_parser_failed_set_by_id(parser, -1, true); + selectors->err_in_function = true; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_selectors_state_function_forgiving(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return lxb_css_selectors_state_forgiving_cb(parser, token, ctx, + lxb_css_selectors_state_complex_list, + failed); +} + +lxb_status_t +lxb_css_selectors_state_function_forgiving_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return lxb_css_selectors_state_forgiving_cb(parser, token, ctx, + lxb_css_selectors_state_relative_list, + failed); +} + +static lxb_status_t +lxb_css_selectors_state_forgiving_cb(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, lxb_css_parser_state_f state, + bool failed) +{ + bool cy; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + + lxb_css_parser_set_ok(parser); + + if (token->type == LXB_CSS_SYNTAX_TOKEN__EOF) { + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, + "%s. End Of File in pseudo function", + lxb_css_selectors_module_name); + } + + if (selectors->list_last == NULL) { + lxb_css_selectors_state_restore_parent(selectors, ctx); + goto empty; + } + + if (selectors->parent->u.pseudo.type + == LXB_CSS_SELECTOR_PSEUDO_CLASS_FUNCTION_WHERE) + { + selectors->list_last->specificity = 0; + } + + lxb_css_selectors_state_restore_parent(selectors, ctx); + + return LXB_STATUS_OK; + +empty: + + selector = selectors->list_last->last; + + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + cy = lxb_css_selector_pseudo_function_can_empty(selector->u.pseudo.type, + cy); + if (cy) { + return LXB_STATUS_OK; + } + + (void) lxb_css_log_format(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, + "%s. Pseudo function can't be empty: %S()", + lxb_css_selectors_module_name, &selector->name); + + lxb_css_selector_remove(selector); + lxb_css_selector_destroy(selector); + + lxb_css_parser_failed_set_by_id(parser, -1, true); + selectors->err_in_function = true; + + return LXB_STATUS_OK; +} + +static void +lxb_css_selectors_state_restore_parent(lxb_css_selectors_t *selectors, + lxb_css_selector_list_t *last) +{ + uint32_t src, dst; + + if (selectors->list_last != NULL && selectors->list_last != last) { + dst = last->specificity; + src = selectors->list_last->specificity; + + selectors->list_last = 0; + + if (last->parent == NULL) { + lxb_css_selector_sp_add_a(dst, lxb_css_selector_sp_a(src)); + lxb_css_selector_sp_add_b(dst, lxb_css_selector_sp_b(src)); + lxb_css_selector_sp_add_c(dst, lxb_css_selector_sp_c(src)); + } + else if (selectors->combinator == LXB_CSS_SELECTOR_COMBINATOR_CLOSE) { + dst |= src; + } + else if (src > dst) { + dst = src; + } + + last->specificity = dst; + } + + if (selectors->list != NULL) { + last->last->u.pseudo.data = selectors->list; + } + + selectors->list_last = last; + + /* Get first Selector in chain. */ + while (last->prev != NULL) { + last = last->prev; + } + + selectors->list = last; + selectors->parent = last->parent; + + lxb_css_selectors_state_restore_combinator(selectors); +} + +static bool +lxb_css_selectors_state_list_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_parser_state_f state) +{ + lxb_css_parser_state_t *states; + lxb_css_selectors_t *selectors = parser->selectors; + + if (lxb_css_parser_is_failed(parser)) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + if (selectors->parent != NULL && selectors->list_last && + selectors->list_last->prev != NULL) + { + lxb_css_selectors_state_func_specificity(selectors); + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COMMA) { + states = lxb_css_parser_states_current(parser); + + if (states->root) { + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + (void) lxb_css_parser_states_pop(parser); + return lxb_css_parser_success(parser); + } + + return lxb_css_selectors_done(parser); + } + + selectors->combinator = selectors->comb_default; + + lxb_css_syntax_token_consume(parser->tkz); + lxb_css_parser_state_set(parser, state); + lxb_css_parser_set_ok(parser); + + return true; +} + +static bool +lxb_css_selectors_state_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_parser_state_t *states; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + } + + if (lxb_css_parser_is_failed(parser)) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + states = lxb_css_parser_states_current(parser); + + if (states->root) { + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + token = lxb_css_selectors_state_function_error(parser, token); + if (token == NULL) { + return lxb_css_parser_fail(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + } + + (void) lxb_css_parser_states_pop(parser); + return lxb_css_parser_success(parser); + } + + return lxb_css_selectors_done(parser); +} + + +static const lxb_css_syntax_token_t * +lxb_css_selectors_state_function_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + bool cy, comma; + lxb_css_selector_list_t *list; + lxb_css_selector_t *selector; + lxb_css_selectors_t *selectors = parser->selectors; + const lxb_css_syntax_token_t *origin; + const lxb_css_selectors_pseudo_data_func_t *func; + + cy = false; + comma = true; + list = selectors->list_last; + selector = selectors->parent; + + if (selector != NULL) { + cy = selector->type == LXB_CSS_SELECTOR_TYPE_PSEUDO_CLASS_FUNCTION; + + func = lxb_css_selector_pseudo_function_by_id(selector->u.pseudo.type, + cy); + if (func == NULL) { + return NULL; + } + + cy = func->forgiving; + comma = func->comma; + } + + if (!selectors->err_in_function) { + origin = lxb_css_syntax_token(parser->tkz); + if (origin == NULL) { + return NULL; + } + + if (token->type != LXB_CSS_SYNTAX_TOKEN__END) { + origin = token; + } + else if (origin->type != LXB_CSS_SYNTAX_TOKEN__EOF) { + origin = NULL; + } + + if (origin != NULL) { + if (lxb_css_syntax_token_error(parser, origin, + "Selectors") == NULL) + { + return NULL; + } + } + } + + selectors->err_in_function = false; + + if (cy) { + lxb_css_selector_list_selectors_remove(selectors, list); + lxb_css_selector_list_destroy(list); + + while (token != NULL + && token->type != LXB_CSS_SYNTAX_TOKEN__END) + { + if (comma == true + && token->type == LXB_CSS_SYNTAX_TOKEN_COMMA + && lxb_css_parser_rule_deep(parser) == 0) + { + break; + } + + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return token; + } + + lxb_css_selector_list_destroy_chain(selectors->list); + + selectors->list = NULL; + selectors->list_last = NULL; + + while (token != NULL + && token->type != LXB_CSS_SYNTAX_TOKEN__END) + { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return token; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.h new file mode 100644 index 0000000..3f10c8f --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/selectors/state.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SELECTORS_STATE_H +#define LEXBOR_CSS_SELECTORS_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/parser.h" +#include "lexbor/css/selectors/base.h" + + +LXB_API bool +lxb_css_selectors_state_complex_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +LXB_API bool +lxb_css_selectors_state_compound_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +LXB_API bool +lxb_css_selectors_state_simple_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +LXB_API bool +lxb_css_selectors_state_relative_list(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +LXB_API bool +lxb_css_selectors_state_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +LXB_API bool +lxb_css_selectors_state_complex(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_compound(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_selectors_state_simple(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +LXB_API lxb_status_t +lxb_css_selectors_state_function_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API lxb_status_t +lxb_css_selectors_state_function_forgiving(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +LXB_API lxb_status_t +lxb_css_selectors_state_function_forgiving_relative(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +lxb_inline bool +lxb_css_selectors_state_fail(lxb_css_parser_t *parser) +{ + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + (void) lxb_css_parser_states_pop(parser); + + return false; +} + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SELECTORS_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.c new file mode 100644 index 0000000..dda5310 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.c @@ -0,0 +1,676 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/state.h" +#include "lexbor/css/css.h" +#include "lexbor/css/at_rule/state.h" + + +static bool +lxb_css_state_list_rules_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_state_list_rules_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_at_rule_t * +lxb_css_state_at_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule); + +static bool +lxb_css_state_at_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_state_at_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static bool +lxb_css_state_at_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t +lxb_css_state_at_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_state_qualified_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static bool +lxb_css_state_qualified_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); + +static lxb_status_t +lxb_css_state_qualified_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_block_t * +lxb_css_state_qualified_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static bool +lxb_css_state_qualified_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx); +static lxb_status_t +lxb_css_state_qualified_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); +static bool +lxb_css_state_block_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_state_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static const lxb_css_syntax_cb_declarations_t * +lxb_css_state_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static lxb_css_parser_state_f +lxb_css_state_declaration_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +static lxb_status_t +lxb_css_state_declaration_end(lxb_css_parser_t *parser, + void *declarations, void *ctx, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed); + +static lxb_status_t +lxb_css_state_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static bool +lxb_css_state_declarations_bad(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + + +static const lxb_css_syntax_cb_list_rules_t lxb_css_state_list_rules = { + .at_rule = lxb_css_state_at_rule_begin, + .qualified_rule = lxb_css_state_qualified_rule_begin, + .next = lxb_css_state_list_rules_next, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_state_list_rules_end +}; + +static const lxb_css_syntax_cb_at_rule_t lxb_css_state_at_rule = { + .prelude = lxb_css_state_at_rule_prelude, + .prelude_end = lxb_css_state_at_rule_prelude_end, + .block = lxb_css_state_at_rule_block_begin, + .cb.failed = lxb_css_state_at_rule_prelude_failed, + .cb.end = lxb_css_state_at_rule_end +}; + +static const lxb_css_syntax_cb_qualified_rule_t lxb_css_state_qualified_rule = { + .prelude = lxb_css_state_qualified_rule_prelude, + .prelude_end = lxb_css_state_qualified_rule_prelude_end, + .block = lxb_css_state_qualified_rule_block_begin, + .cb.failed = lxb_css_state_qualified_rule_prelude_failed, + .cb.end = lxb_css_state_qualified_rule_end +}; + +static const lxb_css_syntax_cb_block_t lxb_css_state_block = { + .at_rule = lxb_css_state_at_rule_begin, + .declarations = lxb_css_state_declarations_begin, + .qualified_rule = lxb_css_state_qualified_rule_begin, + .next = lxb_css_state_block_next, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_state_block_end, +}; + +static const lxb_css_syntax_cb_declarations_t lxb_css_state_declaration = { + .name = lxb_css_state_declaration_name, + .end = lxb_css_state_declaration_end, + .cb.failed = lxb_css_state_declarations_bad, + .cb.end = lxb_css_state_declarations_end +}; + + +bool +lxb_css_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + return true; + + case LXB_CSS_SYNTAX_TOKEN__END: + return true; + + default: + break; + } + + return lxb_css_parser_failed(parser); +} + +bool +lxb_css_state_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + if (token->type == LXB_CSS_SYNTAX_TOKEN__END) { + return lxb_css_parser_success(parser); + } + + /* The lxb_css_syntax_parser_consume(...) locked in this state. */ + + lxb_css_syntax_token_consume(parser->tkz); + + return true; +} + +bool +lxb_css_state_stop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_stop(parser); +} + +bool +lxb_css_state_blank(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + return lxb_css_parser_success(parser); +} + +const lxb_css_syntax_cb_list_rules_t * +lxb_css_state_cb_list_rules(void) +{ + return &lxb_css_state_list_rules; +} + +const lxb_css_syntax_cb_at_rule_t * +lxb_css_state_cb_at_rule(void) +{ + return &lxb_css_state_at_rule; +} + +const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_state_cb_qualified_rule(void) +{ + return &lxb_css_state_qualified_rule; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_state_cb_block(void) +{ + return &lxb_css_state_block; +} + +const lxb_css_syntax_cb_declarations_t * +lxb_css_state_cb_declarations(void) +{ + return &lxb_css_state_declaration; +} + +static bool +lxb_css_state_list_rules_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + void *returned = lxb_css_syntax_returned(parser); + lxb_css_rule_list_t *list = ctx; + + if (returned != NULL) { + lxb_css_rule_list_append(list, returned); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_state_list_rules_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_list_t *list = ctx; + + lxb_css_syntax_set_return(parser, list); + + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_at_rule_t * +lxb_css_state_at_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx, + void **out_rule) +{ + lxb_css_rule_at_t *at; + const lxb_css_entry_at_rule_data_t *entry; + + at = lxb_css_at_rule_create(parser, lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length, + &entry); + if (at == NULL) { + return lxb_css_parser_memory_fail_null(parser); + } + + *out_rule = at; + + return entry->cbs; +} + +static bool +lxb_css_state_at_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_rule_at_t *at = ctx; + + at->prelude_begin = token->offset; + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_state_at_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_status_t status; + lxb_css_rule_at_t *at = ctx; + lxb_css_at_rule__custom_t *custom; + + at->prelude_end = token->offset; + + custom = at->u.custom; + + status = lxb_css_make_data(parser, &custom->prelude, at->prelude_begin, + at->prelude_end); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail_status(parser); + } + + return LXB_STATUS_OK; +} + +const lxb_css_syntax_cb_block_t * +lxb_css_state_at_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lxb_css_rule_list_t *list; + + list = lxb_css_rule_list_create(parser->memory); + if (list == NULL) { + return lxb_css_parser_memory_fail_null(parser); + } + + *out_rule = list; + + return &lxb_css_state_block; +} + +static bool +lxb_css_state_at_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_state_at_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_at_t *at = ctx; + lxb_css_rule_list_t *block = lxb_css_syntax_returned(parser); + + at->u.custom->block = block; + + lxb_css_syntax_set_return(parser, at); + + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_state_qualified_rule_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lxb_css_rule_style_t *style; + + style = lxb_css_rule_style_create(parser->memory); + if (style == NULL) { + return lxb_css_parser_memory_fail_null(parser); + } + + *out_rule = style; + + return &lxb_css_state_qualified_rule; +} + +static bool +lxb_css_state_qualified_rule_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + lxb_css_rule_style_t *style = ctx; + + lxb_css_selectors_clean(parser->selectors); + lxb_css_parser_state_set(parser, lxb_css_selectors_state_complex_list); + + style->prelude_begin = token->offset; + + return false; +} + +static lxb_status_t +lxb_css_state_qualified_rule_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + size_t begin; + lxb_status_t status; + lxb_css_rule_style_t *style = ctx; + lxb_css_rule_bad_style_t *bad; + + if (!failed) { + style->selector = parser->selectors->list; + style->prelude_end = token->offset; + + return LXB_STATUS_OK; + } + + begin = style->prelude_begin; + + (void) lxb_css_rule_style_destroy(style, true); + + bad = lxb_css_rule_bad_style_create(parser->memory); + if (bad == NULL) { + return lxb_css_parser_memory_fail_status(parser); + } + + bad->prelude_begin = begin; + bad->prelude_end = token->offset; + + status = lxb_css_make_data(parser, &bad->selectors, bad->prelude_begin, + bad->prelude_end); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail_status(parser); + } + + lxb_css_parser_set_context(parser, bad); + + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_block_t * +lxb_css_state_qualified_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lxb_css_rule_list_t *list; + + list = lxb_css_rule_list_create(parser->memory); + if (list == NULL) { + return lxb_css_parser_memory_fail_null(parser); + } + + *out_rule = list; + + return &lxb_css_state_block; +} + +static bool +lxb_css_state_qualified_rule_prelude_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx) +{ + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_state_qualified_rule_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_t *declrs; + lxb_css_rule_list_t *list; + lxb_css_rule_style_t *style; + lxb_css_rule_bad_style_t *bad; + + list = lxb_css_syntax_returned(parser); + + if (!failed) { + style = ctx; + + if (list != NULL && list->first != NULL + && list->first->type == LXB_CSS_RULE_DECLARATION_LIST) + { + declrs = list->first; + style->declarations = lxb_css_rule_declaration_list(declrs); + + declrs->parent = lxb_css_rule(style); + + if (list->first == list->last) { + list->last = NULL; + } + + if (declrs->next != NULL) { + declrs->next->prev = NULL; + } + + list->first = declrs->next; + declrs->next = NULL; + } + + style->child = list; + + lxb_css_syntax_set_return(parser, style); + } + else { + bad = ctx; + + if (list != NULL && list->first != NULL + && list->first->type == LXB_CSS_RULE_DECLARATION_LIST) + { + declrs = list->first; + bad->declarations = lxb_css_rule_declaration_list(declrs); + + declrs->parent = lxb_css_rule(bad); + + if (list->first == list->last) { + list->last = NULL; + } + + if (declrs->next != NULL) { + declrs->next->prev = NULL; + } + + list->first = declrs->next; + declrs->next = NULL; + } + + bad->child = list; + + lxb_css_syntax_set_return(parser, bad); + } + + return LXB_STATUS_OK; +} + +static bool +lxb_css_state_block_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + /* rule can be either a declaration list or an at rule. */ + + lxb_css_rule_t *rule = lxb_css_syntax_returned(parser); + lxb_css_rule_list_t *list = ctx; + + lxb_css_rule_list_append(list, rule); + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_state_block_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_list_t *list = ctx; + + lxb_css_syntax_set_return(parser, list); + + return LXB_STATUS_OK; +} + +static const lxb_css_syntax_cb_declarations_t * +lxb_css_state_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + lxb_css_rule_declaration_list_t *list; + + list = lxb_css_rule_declaration_list_create(parser->memory); + if (list == NULL) { + return lxb_css_parser_memory_fail_null(parser); + } + + *out_rule = list; + + return &lxb_css_state_declaration; +} + +static lxb_css_parser_state_f +lxb_css_state_declaration_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule) +{ + const lxb_css_entry_data_t *entry; + lxb_css_rule_declaration_t *declar; + + declar = lxb_css_declaration_create(parser, + lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_token_ident(token)->length, + &entry); + if (declar == NULL) { + (void) lxb_css_parser_memory_fail_null(parser); + return NULL; + } + + /* We present an original position. */ + + declar->offset.name_begin = token->offset; + declar->offset.name_end = token->offset + + lxb_css_syntax_token_base(token)->length; + *out_rule = declar; + + return entry->state; +} + +static lxb_status_t +lxb_css_state_declaration_end(lxb_css_parser_t *parser, + void *declarations, void *ctx, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed) +{ + lxb_status_t status; + lxb_css_property__undef_t *undef; + lxb_css_rule_declaration_t *declar = ctx; + lxb_css_rule_declaration_list_t *list = declarations; + + declar->offset.value_begin = offset->value_begin; + declar->offset.value_end = offset->value_end; + declar->offset.important_begin = offset->important_begin; + declar->offset.important_end = offset->important_end; + declar->important = important; + + if (failed) { + lxb_css_rule_declaration_destroy(declar, false); + + undef = lxb_css_property__undef_create(parser->memory); + if (undef == NULL) { + return lxb_css_parser_memory_fail_status(parser); + } + + undef->type = declar->type; + + status = lxb_css_make_data(parser, &undef->value, + declar->offset.value_begin, + declar->offset.value_end); + if (status != LXB_STATUS_OK) { + return lxb_css_parser_memory_fail_status(parser); + } + + declar->u.undef = undef; + declar->type = LXB_CSS_PROPERTY__UNDEF; + } + + lxb_css_rule_declaration_list_append(list, lxb_css_rule(declar)); + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_state_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + lxb_css_rule_declaration_list_t *list = ctx; + + lxb_css_syntax_set_return(parser, list); + + return LXB_STATUS_OK; +} + +static bool +lxb_css_state_declarations_bad(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + lxb_css_rule_declaration_t *declar; + + if (ctx == NULL) { + declar = lxb_css_rule_declaration_create(parser->memory); + if (declar == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + lxb_css_parser_current_rule(parser)->context = declar; + declar->type = LXB_CSS_PROPERTY__UNDEF; + } + + while (token != NULL && token->type != LXB_CSS_SYNTAX_TOKEN__END) { + lxb_css_syntax_parser_consume(parser); + token = lxb_css_syntax_parser_token(parser); + } + + return lxb_css_parser_success(parser); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.h new file mode 100644 index 0000000..6ecd503 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/state.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_STATE_H +#define LXB_CSS_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/syntax/syntax.h" + + +LXB_API bool +lxb_css_state_success(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_state_failed(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_state_stop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API bool +lxb_css_state_blank(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +LXB_API const lxb_css_syntax_cb_list_rules_t * +lxb_css_state_cb_list_rules(void); + +LXB_API const lxb_css_syntax_cb_at_rule_t * +lxb_css_state_cb_at_rule(void); + +LXB_API const lxb_css_syntax_cb_qualified_rule_t * +lxb_css_state_cb_qualified_rule(void); + +LXB_API const lxb_css_syntax_cb_block_t * +lxb_css_state_cb_block(void); + +LXB_API const lxb_css_syntax_cb_declarations_t * +lxb_css_state_cb_declarations(void); + +const lxb_css_syntax_cb_block_t * +lxb_css_state_at_rule_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.c new file mode 100644 index 0000000..ac16f7b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.c @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/stylesheet.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/at_rule.h" +#include "lexbor/css/property.h" +#include "lexbor/css/rule.h" +#include "lexbor/css/state.h" +#include "lexbor/css/selectors/selectors.h" +#include "lexbor/css/selectors/state.h" + + +lxb_css_stylesheet_t * +lxb_css_stylesheet_create(lxb_css_memory_t *memory) +{ + lxb_status_t status; + lxb_css_stylesheet_t *stylesheet; + + if (memory == NULL) { + memory = lxb_css_memory_create(); + status = lxb_css_memory_init(memory, 1024); + + if (status != LXB_STATUS_OK) { + (void) lxb_css_memory_destroy(memory, true); + return NULL; + } + } + else { + (void) lxb_css_memory_ref_inc(memory); + } + + stylesheet = lexbor_mraw_calloc(memory->mraw, sizeof(lxb_css_stylesheet_t)); + if (stylesheet == NULL) { + return NULL; + } + + stylesheet->memory = memory; + + return stylesheet; +} + +lxb_css_stylesheet_t * +lxb_css_stylesheet_destroy(lxb_css_stylesheet_t *sst, bool destroy_memory) +{ + if (sst == NULL) { + return NULL; + } + + if (destroy_memory) { + (void) lxb_css_memory_ref_dec_destroy(sst->memory); + return NULL; + } + + if (sst->root != NULL) { + (void) lxb_css_rule_destroy(sst->root, true); + } + + (void) lexbor_mraw_free(sst->memory->mraw, sst); + + return NULL; +} + +lxb_status_t +lxb_css_stylesheet_parse(lxb_css_stylesheet_t *sst, lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + lxb_status_t status; + lxb_css_rule_list_t *list; + lxb_css_selectors_t selectors; + + if (sst == NULL || parser == NULL) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + if (parser->selectors == NULL) { + status = lxb_css_selectors_init(&selectors); + if (status != LXB_STATUS_OK) { + return status; + } + + parser->selectors = &selectors; + } + else { + lxb_css_selectors_clean(parser->selectors); + } + + parser->memory = sst->memory; + + list = lxb_css_syntax_parse_list_rules(parser, + lxb_css_state_cb_list_rules(), + data, length); + + if (parser->selectors == &selectors) { + lxb_css_selectors_destroy(&selectors, false); + parser->selectors = NULL; + } + + if (list == NULL) { + sst->root = NULL; + return parser->status; + } + + sst->root = &list->rule; + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.h new file mode 100644 index 0000000..9d52f98 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/stylesheet.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_STYLESHEET_H +#define LXB_CSS_STYLESHEET_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/rule.h" +#include "lexbor/core/mraw.h" +#include "lexbor/css/at_rule.h" +#include "lexbor/css/syntax/tokenizer.h" +#include "lexbor/css/selectors/base.h" + + +struct lxb_css_stylesheet { + lxb_css_rule_t *root; + lxb_css_memory_t *memory; + + void *element; /* lxb_html_style_element_t * */ +}; + +/* + * Create a new CSS stylesheet object. + * + * This function creates a stylesheet object which holds the parsed CSS rules. + * + * @param[in] memory Optional. A memory pool to use for allocations. + * If NULL, a new internal memory pool is created/managed + * by the stylesheet. + * + * @return A new lxb_css_stylesheet_t * or NULL on failure. + */ +LXB_API lxb_css_stylesheet_t * +lxb_css_stylesheet_create(lxb_css_memory_t *memory); + +/* + * Destroy a CSS stylesheet object. + * + * @param[in] sst Optional. The stylesheet object to destroy. + * If NULL, the function returns NULL. + * @param[in] destroy_memory If true, the memory pool attached to + * the stylesheet is also destroyed. + * + * @return Always NULL. + */ +LXB_API lxb_css_stylesheet_t * +lxb_css_stylesheet_destroy(lxb_css_stylesheet_t *sst, bool destroy_memory); + +/* + * Parse CSS content into the stylesheet. + * + * This function takes CSS text and builds the rule tree in the stylesheet. + * + * The function returns an error only in extremely unforeseen circumstances, + * such as the inability to allocate memory for objects. Any broken CSS will + * not cause an error. + * + * Selectors note: + * If the provided parser does not have an initialized selectors module, one + * will be created temporarily for this call. For better performance when + * parsing multiple stylesheets, initialize the selectors module in + * the parser once: + * + * lxb_css_parser_t *parser = lxb_css_parser_create(); + * lxb_css_parser_init(parser, NULL); + * lxb_css_parser_selectors_init(parser); + * + * @param[in] sst Required. The target stylesheet. + * @param[in] parser Required. An initialized CSS parser. + * @param[in] data Optional. The CSS text data. + * @param[in] length Required. Length of the data in bytes. + * + * @return LXB_STATUS_OK on success, or an error code on failure. + */ +LXB_API lxb_status_t +lxb_css_stylesheet_parse(lxb_css_stylesheet_t *sst, lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_STYLESHEET_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.c new file mode 100644 index 0000000..d3f7c7b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.c @@ -0,0 +1,486 @@ +/* + * Copyright (C) 2021-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/conv.h" +#include "lexbor/core/serialize.h" +#include "lexbor/css/css.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/syntax/anb.h" + + +static bool +lxb_css_syntax_anb_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx); + +static lxb_status_t +lxb_css_syntax_anb_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +static lxb_css_log_message_t * +lxb_css_syntax_anb_fail(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token); + +static lxb_status_t +lxb_css_syntax_anb_state_ident(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb); + +static lxb_status_t +lxb_css_syntax_anb_state_ident_data(lxb_css_parser_t *parser, + lxb_css_syntax_anb_t *anb, + const lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + + +static const lxb_css_syntax_cb_pipe_t lxb_css_syntax_anb_pipe = { + .prelude = lxb_css_syntax_anb_state, + .cb.failed = lxb_css_state_failed, + .cb.end = lxb_css_syntax_anb_end +}; + + +lxb_css_syntax_anb_t +lxb_css_syntax_anb_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length) +{ + lxb_status_t status; + lxb_css_syntax_anb_t anb; + lxb_css_syntax_rule_t *rule; + + memset(&anb, 0, sizeof(lxb_css_syntax_anb_t)); + + if (parser->stage != LXB_CSS_PARSER_CLEAN) { + if (parser->stage == LXB_CSS_PARSER_RUN) { + parser->status = LXB_STATUS_ERROR_WRONG_ARGS; + return anb; + } + + lxb_css_parser_clean(parser); + } + + lxb_css_parser_buffer_set(parser, data, length); + + rule = lxb_css_syntax_parser_pipe_push(parser, &lxb_css_syntax_anb_pipe, + NULL, &anb, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + return anb; + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + status = lxb_css_syntax_parser_run(parser); + if (status != LXB_STATUS_OK) { + /* Destroy. */ + } + + parser->stage = LXB_CSS_PARSER_END; + + return anb; +} + +static bool +lxb_css_syntax_anb_state(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, void *ctx) +{ + parser->status = lxb_css_syntax_anb_handler(parser, token, ctx); + + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + return lxb_css_parser_memory_fail(parser); + } + + if (parser->status != LXB_STATUS_OK + || (token->type != LXB_CSS_SYNTAX_TOKEN__END)) + { + (void) lxb_css_syntax_anb_fail(parser, token); + } + + return lxb_css_parser_success(parser); +} + +static lxb_status_t +lxb_css_syntax_anb_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed) +{ + return LXB_STATUS_OK; +} + +static lxb_css_log_message_t * +lxb_css_syntax_anb_fail(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + parser->status = LXB_STATUS_ERROR_UNEXPECTED_DATA; + + static const char anb[] = "An+B"; + + return lxb_css_syntax_token_error(parser, token, anb); +} + +lxb_status_t +lxb_css_syntax_anb_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb) +{ + const lxb_char_t *data, *end; + lxb_css_syntax_token_ident_t *ident; + +again: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + if (lxb_css_syntax_token_dimension(token)->num.is_float) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = lexbor_conv_double_to_long(lxb_css_syntax_token_dimension(token)->num.num); + + ident = lxb_css_syntax_token_dimension_string(token); + + goto ident; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + return lxb_css_syntax_anb_state_ident(parser, token, anb); + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (lxb_css_syntax_token_number(token)->is_float) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = 0; + anb->b = lexbor_conv_double_to_long(lxb_css_syntax_token_number(token)->num); + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '+') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = 1; + + ident = lxb_css_syntax_token_ident(token); + + goto ident; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_m(parser, token); + goto again; + + default: + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; + +ident: + + data = ident->data; + end = ident->data + ident->length; + + if (*data != 'n' && *data != 'N') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + data++; + + return lxb_css_syntax_anb_state_ident_data(parser, anb, token, data, end); +} + +static lxb_status_t +lxb_css_syntax_anb_state_ident(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb) +{ + size_t length; + lxb_char_t c; + const lxb_char_t *data, *end; + lxb_css_syntax_token_ident_t *ident; + + static const lxb_char_t odd[] = "odd"; + static const lxb_char_t even[] = "even"; + + ident = lxb_css_syntax_token_ident(token); + + length = ident->length; + data = ident->data; + end = data + length; + + c = *data++; + + /* 'n' or '-n' */ + + if (c == 'n' || c == 'N') { + anb->a = 1; + } + else if (c == '-') { + if (data >= end) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + c = *data++; + + if (c != 'n' && c != 'N') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->a = -1; + } + else if (length == sizeof(odd) - 1 + && lexbor_str_data_ncasecmp(ident->data, odd, sizeof(odd) - 1)) + { + anb->a = 2; + anb->b = 1; + + lxb_css_syntax_parser_consume(parser); + return LXB_STATUS_OK; + } + else if (length == sizeof(even) - 1 + && lexbor_str_data_ncasecmp(ident->data, even, sizeof(even) - 1)) + { + anb->a = 2; + anb->b = 0; + + lxb_css_syntax_parser_consume(parser); + return LXB_STATUS_OK; + } + else { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + return lxb_css_syntax_anb_state_ident_data(parser, anb, token, data, end); +} + +static lxb_status_t +lxb_css_syntax_anb_state_ident_data(lxb_css_parser_t *parser, + lxb_css_syntax_anb_t *anb, + const lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + unsigned sign; + lxb_char_t c; + const lxb_char_t *p; + + sign = 0; + + if (data >= end) { + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + if (!lxb_css_syntax_token_number(token)->have_sign) { + anb->b = 0; + return LXB_STATUS_OK; + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + c = lxb_css_syntax_token_delim(token)->character; + + switch (c) { + case '-': + sign = 1; + break; + + case '+': + sign = 2; + break; + + default: + anb->b = 0; + return LXB_STATUS_OK; + } + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + + break; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + anb->b = 0; + return LXB_STATUS_OK; + + default: + anb->b = 0; + return LXB_STATUS_OK; + } + + goto number; + } + + c = *data++; + + if (c != '-') { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (data < end) { + p = data; + anb->b = -lexbor_conv_data_to_long(&data, end - data); + + if (anb->b > 0 || data == p || data < end) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + goto done; + } + + sign = 1; + + lxb_css_syntax_parser_consume(parser); + lxb_css_parser_token_status_wo_ws_m(parser, token); + +number: + + if (token->type != LXB_CSS_SYNTAX_TOKEN_NUMBER) { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + if (lxb_css_syntax_token_number(token)->is_float + || (sign > 0 && lxb_css_syntax_token_number(token)->have_sign)) + { + return LXB_STATUS_ERROR_UNEXPECTED_DATA; + } + + anb->b = lexbor_conv_double_to_long(lxb_css_syntax_token_number(token)->num); + + if (sign == 1) { + anb->b = -anb->b; + } + +done: + + lxb_css_syntax_parser_consume(parser); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_anb_serialize(lxb_css_syntax_anb_t *anb, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t buf[128]; + lxb_char_t *p, *end; + + if (anb == NULL) { + return LXB_STATUS_OK; + } + + static const lxb_char_t odd[] = "odd"; + static const lxb_char_t even[] = "even"; + + if (anb->a == 2) { + if (anb->b == 1) { + return cb(odd, sizeof(odd) - 1, ctx); + } + + if (anb->b == 0) { + return cb(even, sizeof(even) - 1, ctx); + } + } + + p = buf; + end = p + sizeof(buf); + + if (anb->a == 1) { + *p = '+'; + p++; + } + else if (anb->a == -1) { + *p = '-'; + p++; + } + else { + p += lexbor_conv_float_to_data((double) anb->a, p, end - p); + if (p >= end) { + return LXB_STATUS_ERROR_SMALL_BUFFER; + } + } + + *p = 'n'; + p++; + + if (p >= end) { + return cb(buf, p - buf, ctx); + } + + if (anb->b == 0) { + return cb(buf, p - buf, ctx); + } + + if (anb->b > 0) { + *p = '+'; + p++; + + if (p >= end) { + return LXB_STATUS_ERROR_SMALL_BUFFER; + } + } + + p += lexbor_conv_float_to_data((double) anb->b, p, end - p); + + return cb(buf, p - buf, ctx); +} + +lxb_char_t * +lxb_css_syntax_anb_serialize_char(lxb_css_syntax_anb_t *anb, size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_syntax_anb_serialize(anb, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_syntax_anb_serialize(anb, lexbor_serialize_copy_cb, &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.h new file mode 100644 index 0000000..ed6838e --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/anb.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_ANB_H +#define LEXBOR_CSS_SYNTAX_ANB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/syntax/token.h" + + +typedef struct { + long a; + long b; +} +lxb_css_syntax_anb_t; + + +LXB_API lxb_css_syntax_anb_t +lxb_css_syntax_anb_parse(lxb_css_parser_t *parser, + const lxb_char_t *data, size_t length); + +LXB_API lxb_status_t +lxb_css_syntax_anb_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_anb_t *anb); + +LXB_API lxb_status_t +lxb_css_syntax_anb_serialize(lxb_css_syntax_anb_t *anb, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_char_t * +lxb_css_syntax_anb_serialize_char(lxb_css_syntax_anb_t *anb, + size_t *out_length); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_ANB_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/base.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/base.h new file mode 100644 index 0000000..627fce4 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/base.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_BASE_H +#define LEXBOR_CSS_SYNTAX_BASE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/core/base.h" +#include "lexbor/css/base.h" + + +#define LXB_CSS_SYNTAX_VERSION_MAJOR 1 +#define LXB_CSS_SYNTAX_VERSION_MINOR 4 +#define LXB_CSS_SYNTAX_VERSION_PATCH 0 + +#define LXB_CSS_SYNTAX_VERSION_STRING \ + LEXBOR_STRINGIZE(LXB_CSS_SYNTAX_VERSION_MAJOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_SYNTAX_VERSION_MINOR) "." \ + LEXBOR_STRINGIZE(LXB_CSS_SYNTAX_VERSION_PATCH) + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_BASE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.c new file mode 100644 index 0000000..7de012a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.c @@ -0,0 +1,2251 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/state.h" +#include "lexbor/css/syntax/parser.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/at_rule/state.h" + + +static const lexbor_str_t lxb_css_err_rc = lexbor_str("Unexpected token in " + "qualified rule: RC_BRACKET"); + +static lxb_css_syntax_rule_t * +lxb_css_syntax_parser_stack_pop(lxb_css_parser_t *parser); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + lxb_css_parser_state_f back_state, + lxb_css_syntax_begin_at_rule_f cb, + bool nested); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_prelude_end_consule_token(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_back_to_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_back_to_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + lxb_css_parser_state_f back_state, + lxb_css_syntax_begin_block_f cb); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_validate(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_drop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_back_to_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_end_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + bool skip_token); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + bool skip_token); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components_back_to_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + + +lxb_inline const lxb_css_syntax_token_t * +lxb_css_syntax_parser_failed(lxb_css_parser_t *parser, lxb_status_t status) +{ + parser->status = status; + return NULL; +} + +lxb_inline const lxb_css_syntax_token_t * +lxb_css_syntax_token_parser_do_phase_again(lxb_css_parser_t *parser) +{ + parser->fake_null = true; + return NULL; +} + +lxb_inline const lxb_css_syntax_token_t * +lxb_css_syntax_parser_call_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token) +{ + lxb_css_syntax_rule_t *rules; + + (void) lxb_css_syntax_parser_stack_pop(parser); + + rules = parser->rules; + + if (parser->rules <= parser->rules_begin) { + rules->state = lxb_css_state_stop; + return token; + } + + return rules->back(parser, token, rules); +} + + +lxb_status_t +lxb_css_syntax_parser_run(lxb_css_parser_t *parser) +{ + const lxb_css_syntax_token_t *token; + + parser->loop = true; + + do { + token = lxb_css_syntax_parser_token(parser); + if (token == NULL) { + if (parser->fake_null) { + parser->fake_null = false; + continue; + } + + return parser->status; + } + + while (parser->rules->state(parser, token, + parser->rules->context) == false) {}; + } + while (parser->loop); + + return parser->status; +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token(lxb_css_parser_t *parser) +{ + lxb_css_syntax_token_t *token; + lxb_css_syntax_rule_t *rule = parser->rules; + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + return rule->phase(parser, token, rule); +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token_wo_ws(lxb_css_parser_t *parser) +{ + const lxb_css_syntax_token_t *token; + + token = lxb_css_syntax_parser_token(parser); + + if (token != NULL && token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_parser_consume(parser); + return lxb_css_syntax_parser_token(parser); + } + + return token; +} + +void +lxb_css_syntax_parser_consume(lxb_css_parser_t *parser) +{ + if (!parser->rules->skip_consume) { + lxb_css_syntax_token_consume(parser->tkz); + } +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_list_rules_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *cb_rules, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_list_rules, + .state = lxb_css_state_blank, + .back = lxb_css_syntax_parser_list_rules_back, + .cbx.list_rules = cb_rules, + .context = ctx, + .block_end = stop + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_at_rule_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_at_rule_t *at_rule, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop, + bool nested) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_at_rule, + .state = at_rule->prelude, + .back = lxb_css_syntax_parser_at_back_to_prelude, + .cbx.at_rule = at_rule, + .context = ctx, + .block_end = stop, + .nested = nested + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_qualified_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_qualified_rule_t *qualified, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop, + bool nested) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_qualified_rule, + .state = qualified->prelude, + .back = lxb_css_syntax_parser_qualified_back_to_prelude, + .cbx.qualified_rule = qualified, + .context = ctx, + .block_end = stop, + .nested = nested + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_block_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_block_t *block, + lxb_css_parser_state_f back, void *ctx) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_block, + .state = lxb_css_state_blank, + .back = lxb_css_syntax_parser_block_back, + .cbx.block = block, + .context = ctx + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_declarations_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_declarations_t *declr, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop, + bool name_validate, bool nested) +{ + lxb_css_syntax_rule_t *rule; + lxb_css_syntax_state_f phase; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + phase = (!name_validate) ? lxb_css_syntax_parser_declarations_begin + : lxb_css_syntax_parser_declarations_validate; + + *rule = (lxb_css_syntax_rule_t) { + .phase = phase, + .state = lxb_css_state_blank, + .back = lxb_css_syntax_parser_declaration_back_to_value, + .cbx.declarations = declr, + .context = ctx, + .block_end = stop, + .nested = nested + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_components_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *comp, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_components, + .state = comp->prelude, + .back = lxb_css_syntax_parser_components_back_to_value, + .cbx.components = comp, + .context = ctx, + .block_end = stop + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_function_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_function_t *func, + lxb_css_parser_state_f back, void *ctx) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_function, + .state = func->value, + .back = lxb_css_syntax_parser_function_back, + .cbx.func = func, + .context = ctx + }; + + return rule; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_parser_pipe_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_pipe_t *pipe, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_rule_t *rule; + + if ((parser->rules + 1) >= parser->rules_end) { + parser->status = lxb_css_syntax_stack_expand(parser, 1); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + } + + parser->rules->back_state = back; + + rule = ++parser->rules; + + *rule = (lxb_css_syntax_rule_t) { + .phase = lxb_css_syntax_parser_pipe, + .state = pipe->prelude, + .back = lxb_css_syntax_parser_pipe_back, + .cbx.pipe = pipe, + .context = ctx, + .block_end = stop + }; + + return rule; +} + +static lxb_css_syntax_rule_t * +lxb_css_syntax_parser_stack_pop(lxb_css_parser_t *parser) +{ + return parser->rules--; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->offset > token->offset) { + return token; + } + + rule->skip_consume = true; + +begin: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + case LXB_CSS_SYNTAX_TOKEN_CDC: + case LXB_CSS_SYNTAX_TOKEN_CDO: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + rule->phase = lxb_css_syntax_parser_list_rules_at; + break; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + rule->phase = lxb_css_syntax_parser_list_rules_qualified; + break; + } + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + + return lxb_css_parser_token_end(parser, 0); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + const lxb_css_syntax_cb_list_rules_t *list_rules = rule->cbx.list_rules; + + return lxb_css_syntax_parser_at_begin(parser, token, rule, list_rules->next, + list_rules->at_rule, false); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + void *qualified_ctx; + lxb_css_syntax_rule_t *qualified; + lxb_css_syntax_begin_qualified_rule_f cb; + const lxb_css_syntax_cb_list_rules_t *list_rules; + const lxb_css_syntax_cb_qualified_rule_t *cb_qualified; + + qualified_ctx = NULL; + list_rules = rule->cbx.list_rules; + cb = list_rules->qualified_rule; + + rule->skip_consume = false; + + cb_qualified = cb(parser, token, rule->context, &qualified_ctx); + if (cb_qualified == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + qualified = lxb_css_syntax_parser_qualified_push(parser, cb_qualified, + list_rules->next, + qualified_ctx, + rule->block_end, + false); + if (qualified == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_list_rules_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_list_rules; + rule->state = rule->back_state; + rule->skip_consume = true; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + lxb_css_parser_state_f back_state, + lxb_css_syntax_begin_at_rule_f cb, + bool nested) +{ + void *at_rule_ctx; + lxb_css_syntax_rule_t *at_rule; + const lxb_css_syntax_cb_at_rule_t *cb_at_rule; + + at_rule_ctx = NULL; + rule->skip_consume = false; + + cb_at_rule = cb(parser, token, rule->context, &at_rule_ctx); + if (cb_at_rule == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + at_rule = lxb_css_syntax_parser_at_rule_push(parser, cb_at_rule, + back_state, at_rule_ctx, + rule->block_end, nested); + if (at_rule == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * First call for this code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + * Rule -- lxb_css_syntax_rule_t with at_rule callbacks. + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + const lxb_css_log_message_t *msg; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_at_prelude_end; + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_at_block; + rule->back = lxb_css_syntax_parser_end_back; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); + } + + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep != 0) { + if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + } + else { + msg = lxb_css_log_format(parser->log, + LXB_CSS_LOG_SYNTAX_ERROR, + "%S", &lxb_css_err_rc); + if (msg == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + if (rule->nested) { + rule->phase = lxb_css_syntax_parser_at_prelude_end; + goto done; + } + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_at_prelude_end_consule_token; + goto done; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + rule->phase = lxb_css_syntax_parser_at_prelude_end; + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + const lxb_css_syntax_cb_at_rule_t *at_rule = rule->cbx.at_rule; + + status = at_rule->prelude_end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + return lxb_css_syntax_parser_block_begin(parser, token, rule, + at_rule->prelude, at_rule->block); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + const lxb_css_syntax_cb_at_rule_t *at_rule = rule->cbx.at_rule; + + rule->phase = lxb_css_syntax_parser_end; + rule->state = lxb_css_state_success; + + status = at_rule->prelude_end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_prelude_end_consule_token(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + const lxb_css_syntax_cb_at_rule_t *at_rule = rule->cbx.at_rule; + + rule->phase = lxb_css_syntax_parser_end_consume_token; + + status = at_rule->prelude_end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_at_back_to_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_at_rule; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + const lxb_css_log_message_t *msg; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_qualified_rule_block; + rule->back = lxb_css_syntax_parser_end_back; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); + } + + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep != 0) { + if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + } + else { + msg = lxb_css_log_format(parser->log, + LXB_CSS_LOG_SYNTAX_ERROR, + "%S", &lxb_css_err_rc); + if (msg == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + if (rule->nested) { + goto done; + } + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_qualified_prelude_end; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_rule_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + const lxb_css_syntax_cb_qualified_rule_t *qualified_rule; + + qualified_rule = rule->cbx.qualified_rule; + + parser->status = qualified_rule->prelude_end(parser, token, + rule->context, rule->failed); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + + return lxb_css_syntax_parser_block_begin(parser, token, rule, + qualified_rule->prelude, + qualified_rule->block); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_prelude_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + const lxb_css_syntax_cb_qualified_rule_t *qualified_rule; + + qualified_rule = rule->cbx.qualified_rule; + + parser->status = qualified_rule->prelude_end(parser, token, + rule->context, rule->failed); + if (parser->status != LXB_STATUS_OK) { + return NULL; + } + + rule->phase = lxb_css_syntax_parser_end; + rule->state = lxb_css_state_success; + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_qualified_back_to_prelude(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_qualified_rule; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + lxb_css_parser_state_f back_state, + lxb_css_syntax_begin_block_f cb) +{ + void *block_ctx; + lxb_css_syntax_rule_t *block; + const lxb_css_syntax_cb_block_t *cb_block; + + block_ctx = NULL; + rule->skip_consume = false; + + cb_block = cb(parser, token, rule->context, &block_ctx); + if (cb_block == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + block = lxb_css_syntax_parser_block_push(parser, cb_block, back_state, + block_ctx); + if (block == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + lxb_css_syntax_token_consume(parser->tkz); + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if (rule->offset > token->offset) { + return token; + } + +begin: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_end_consume_token; + break; + } + + if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + rule->phase = lxb_css_syntax_parser_end; + break; + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + rule->phase = lxb_css_syntax_parser_block_at; + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + if (lxb_css_syntax_tokenizer_lookup_colon(parser->tkz)) { + rule->phase = lxb_css_syntax_parser_block_declarations; + break; + } + + /* Fall Through. */ + + default: + rule->phase = lxb_css_syntax_parser_block_qualified; + break; + } + + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, 0); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_at(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + const lxb_css_syntax_cb_block_t *block = rule->cbx.block; + + return lxb_css_syntax_parser_at_begin(parser, token, rule, + block->next, block->at_rule, true); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + void *declr_ctx; + lxb_css_syntax_rule_t *declr_rule; + lxb_css_syntax_begin_declarations_f cb; + const lxb_css_syntax_cb_block_t *block; + const lxb_css_syntax_cb_declarations_t *cb_declr; + + declr_ctx = NULL; + block = rule->cbx.block; + cb = block->declarations; + + rule->skip_consume = false; + + cb_declr = cb(parser, token, rule->context, &declr_ctx); + if (cb_declr == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + declr_rule = lxb_css_syntax_parser_declarations_push(parser, cb_declr, + block->next, declr_ctx, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET, + false, true); + if (declr_rule == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_qualified(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + void *qualified_ctx; + lxb_css_syntax_rule_t *qualified; + const lxb_css_syntax_cb_block_t *block; + lxb_css_syntax_begin_qualified_rule_f cb; + const lxb_css_syntax_cb_qualified_rule_t *cb_qualified; + + qualified_ctx = NULL; + block = rule->cbx.block; + cb = block->qualified_rule; + + rule->skip_consume = false; + + cb_qualified = cb(parser, token, rule->context, &qualified_ctx); + if (cb_qualified == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + qualified = lxb_css_syntax_parser_qualified_push(parser, cb_qualified, + block->next, qualified_ctx, + LXB_CSS_SYNTAX_TOKEN_SEMICOLON, + true); + if (qualified == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_MEMORY_ALLOCATION); + } + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_block_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_block; + rule->state = rule->back_state; + rule->skip_consume = true; + + return token; +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_validate(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ +begin: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_IDENT: + if (lxb_css_syntax_tokenizer_lookup_colon(parser->tkz)) { + return lxb_css_syntax_parser_declarations_begin(parser, token, rule); + } + + break; + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + rule->phase = lxb_css_syntax_parser_declarations_end; + rule->context_old = rule->context; + + return lxb_css_syntax_token_parser_do_phase_again(parser); + + default: + break; + } + + rule->phase = lxb_css_syntax_parser_declarations_drop; + rule->state = rule->cbx.cb->failed; + rule->begin = token->offset; + rule->context_old = rule->context; + rule->context = NULL; + rule->failed = true; + + parser->offset.value_end = 0; + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_begin(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + void *declr_ctx; + lxb_css_syntax_declaration_name_f cb; + + declr_ctx = NULL; + cb = rule->cbx.declarations->name; + + rule->state = cb(parser, token, rule->context, &declr_ctx); + if (rule->state == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + rule->phase = lxb_css_syntax_parser_declarations_name; + rule->context_old = rule->context; + rule->context = declr_ctx; + rule->skip_consume = false; + + lxb_css_syntax_token_consume(parser->tkz); + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_name(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + /* 1. */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + } + + /* 2. */ + + if (token->type != LXB_CSS_SYNTAX_TOKEN_COLON) { + /* Parse error. */ + + /* + * It can't be. + * + * Before entering the lxb_css_syntax_parser_declarations_name() + * function, data validation takes place. In fact, these checks are not + * needed here. + */ + + /* + * But it's good for validation, if we come here it means we're + * doing badly. + */ + + return NULL; + } + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + /* 3. */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + } + + rule->begin = token->offset; + parser->offset.value_end = 0; + + /* 4. */ + + rule->phase = lxb_css_syntax_parser_declarations_value; + + return lxb_css_syntax_parser_declarations_value(parser, token, rule); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + bool imp; + size_t offset; + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + +again: + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + if (rule->deep != 0) { + return token; + } + + imp = lxb_css_syntax_tokenizer_lookup_declaration_ws_end(parser->tkz, + rule->block_end, + (rule->block_end == LXB_CSS_SYNTAX_TOKEN_RC_BRACKET) ? 0x7D : 0x00); + + if (!imp) { + return token; + } + + parser->offset.value_end = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + /* Have !important? */ + + if (token->type == LXB_CSS_SYNTAX_TOKEN_DELIM) { + rule->important = true; + parser->offset.important_begin = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + /* Skip important */ + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + parser->offset.important_end = token->offset + + lxb_css_syntax_token_base(token)->length; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + } + else { + parser->offset.important_begin = 0; + parser->offset.important_end = 0; + } + + goto again; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_declarations_next; + + offset = token->offset; + + parser->offset.value_begin = rule->begin; + parser->offset.end = offset; + + if (parser->offset.value_end == 0) { + parser->offset.value_end = offset; + parser->offset.important_begin = 0; + parser->offset.important_end = 0; + } + + lxb_css_syntax_token_consume(parser->tkz); + + return lxb_css_parser_token_end(parser, offset); + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '!') { + return token; + } + + imp = lxb_css_syntax_tokenizer_lookup_important(parser->tkz, + rule->block_end, + (rule->block_end == LXB_CSS_SYNTAX_TOKEN_RC_BRACKET) ? 0x7D : 0x00); + + if (!imp) { + return token; + } + + rule->important = true; + + parser->offset.value_end = token->offset; + parser->offset.important_begin = token->offset; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + parser->offset.important_end = token->offset + + lxb_css_syntax_token_base(token)->length; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, parser->tkz->status); + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + + goto again; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + parser->offset.value_begin = rule->begin; + parser->offset.end = token->offset; + + if (parser->offset.value_end == 0) { + parser->offset.value_end = token->offset; + parser->offset.important_begin = 0; + parser->offset.important_end = 0; + } + + rule->phase = lxb_css_syntax_parser_declaration_end; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_drop(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + size_t offset; + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_declarations_next; + + offset = token->offset; + + parser->offset.value_begin = rule->begin; + parser->offset.end = offset; + + if (parser->offset.value_end == 0) { + parser->offset.value_end = offset; + parser->offset.important_begin = 0; + parser->offset.important_end = 0; + } + + lxb_css_syntax_token_consume(parser->tkz); + + return lxb_css_parser_token_end(parser, offset); + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + if (rule->deep == 0) { + if (rule->nested) { + goto done; + } + } + else if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + parser->offset.value_begin = rule->begin; + parser->offset.end = token->offset; + + if (parser->offset.value_end == 0) { + parser->offset.value_end = token->offset; + parser->offset.important_begin = 0; + parser->offset.important_end = 0; + } + + rule->phase = lxb_css_syntax_parser_declaration_end; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_next(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + void *out_rule; + lxb_status_t status; + lxb_css_syntax_declaration_name_f name; + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); + } + + status = rule->cbx.declarations->end(parser, rule->context_old, + rule->context, token, &parser->offset, + rule->important, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + +begin: + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + goto begin; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + rule->phase = lxb_css_syntax_parser_declarations_end; + break; + + case LXB_CSS_SYNTAX_TOKEN_IDENT: + if (lxb_css_syntax_tokenizer_lookup_colon(parser->tkz)) { + out_rule = NULL; + name = rule->cbx.declarations->name; + rule->skip_consume = true; + + rule->state = name(parser, token, rule->context, &out_rule); + if (rule->state == NULL) { + return lxb_css_syntax_parser_failed(parser, + LXB_STATUS_ERROR_UNEXPECTED_DATA); + } + + rule->context = out_rule; + + lxb_css_syntax_token_consume(parser->tkz); + + rule->phase = lxb_css_syntax_parser_declarations_name; + rule->context = out_rule; + + break; + } + + /* Fall through. */ + + default: + if (rule->nested) { + rule->phase = lxb_css_syntax_parser_declarations_end; + } + else { + rule->phase = lxb_css_syntax_parser_declarations_drop; + rule->state = rule->cbx.cb->failed; + rule->begin = token->offset; + rule->context = NULL; + rule->failed = true; + + parser->offset.value_end = 0; + + return lxb_css_syntax_token_parser_do_phase_again(parser); + } + + break; + } + + rule->skip_consume = false; + rule->important = false; + rule->failed = false; + + return lxb_css_syntax_token_parser_do_phase_again(parser); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_back_to_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_declarations_value; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + return lxb_css_syntax_parser_declaration_end_handler(parser, token, + rule, false); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declaration_end_handler(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + bool skip_token) +{ + lxb_status_t status; + + if (rule->state != lxb_css_state_success) { + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); + } + + status = rule->cbx.declarations->end(parser, rule->context_old, + rule->context, token, &parser->offset, + rule->important, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + return lxb_css_syntax_parser_declarations_end_h(parser, token, + rule, skip_token); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + return lxb_css_syntax_parser_declarations_end_h(parser, token, + rule, false); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_declarations_end_h(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule, + bool skip_token) +{ + lxb_status_t status; + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + rule->context = rule->context_old; + + status = rule->cbx.cb->end(parser, token, rule->context, false); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + if (skip_token) { + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + } + + return lxb_css_syntax_parser_call_back(parser, token); +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + if (rule->block_end == token->type && rule->deep == 0) { + goto done; + } + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_components_back_to_value(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_components; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + + if (rule->offset > token->offset) { + return token; + } + + rule->offset = token->offset + lxb_css_syntax_token_base(token)->length; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS); + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + status = lxb_css_parser_types_push(parser, + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET); + break; + + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + if (rule->deep == 0) { + rule->phase = lxb_css_syntax_parser_end_consume_token; + goto done; + } + + if (parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + if (rule->deep != 0 && parser->types_pos[-1] == token->type) { + parser->types_pos--; + rule->deep--; + } + + return token; + + case LXB_CSS_SYNTAX_TOKEN__EOF: + rule->phase = lxb_css_syntax_parser_end; + goto done; + + default: + return token; + } + + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->deep++; + + return token; + +done: + + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_function_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_function; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + if ((rule->block_end == token->type && rule->deep == 0) + || token->type == LXB_CSS_SYNTAX_TOKEN__EOF) + { + rule->phase = lxb_css_syntax_parser_end; + rule->skip_consume = true; + + return lxb_css_parser_token_end(parser, token->offset); + } + + return token; +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_pipe_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_pipe; + rule->state = rule->back_state; + rule->skip_consume = false; + + return token; +} + +/* + * This code will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ +static const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end_back(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + rule->phase = lxb_css_syntax_parser_end; + rule->state = lxb_css_state_blank; + + return token; +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + lxb_css_syntax_cb_base_t *base; + + rule->skip_consume = true; + + if (rule->state != lxb_css_state_success) { + return lxb_css_parser_token_end(parser, token->offset); + } + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + base = rule->cbx.user; + + status = base->end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->skip_consume = false; + + return lxb_css_syntax_parser_call_back(parser, token); +} + +const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end_consume_token(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule) +{ + lxb_status_t status; + lxb_css_syntax_cb_base_t *base; + + rule->skip_consume = true; + + if (rule->state != lxb_css_state_success) { + return lxb_css_parser_token_end(parser, token->offset); + } + + /* This code will be called exclusively from the lxb_css_parser_run(...). */ + + base = rule->cbx.user; + + status = base->end(parser, token, rule->context, rule->failed); + if (status != LXB_STATUS_OK) { + return lxb_css_syntax_parser_failed(parser, status); + } + + rule->skip_consume = false; + + lxb_css_syntax_token_consume(parser->tkz); + + token = lxb_css_syntax_token(parser->tkz); + if (token == NULL) { + return lxb_css_syntax_parser_failed(parser, + parser->tkz->status); + } + + return lxb_css_syntax_parser_call_back(parser, token); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.h new file mode 100644 index 0000000..524da6b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/parser.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2020-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_PARSER_H +#define LEXBOR_CSS_SYNTAX_PARSER_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "lexbor/css/base.h" + + +LXB_API lxb_status_t +lxb_css_syntax_parser_run(lxb_css_parser_t *parser); + +LXB_API const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token(lxb_css_parser_t *parser); + +LXB_API const lxb_css_syntax_token_t * +lxb_css_syntax_parser_token_wo_ws(lxb_css_parser_t *parser); + +LXB_API void +lxb_css_syntax_parser_consume(lxb_css_parser_t *parser); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_list_rules_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *list_rules, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_at_rule_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_at_rule_t *at_rule, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop, + bool nested); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_qualified_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_qualified_rule_t *qualified, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop, + bool nested); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_block_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_block_t *block, + lxb_css_parser_state_f back, void *ctx); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_declarations_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_declarations_t *declr, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop, + bool name_validate, bool nested); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_components_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_components_t *comp, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_function_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_function_t *func, + lxb_css_parser_state_f back, void *ctx); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_parser_pipe_push(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_pipe_t *pipe, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop); + +LXB_API const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + +LXB_API const lxb_css_syntax_token_t * +lxb_css_syntax_parser_end_consume_token(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_PARSER_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/res.h new file mode 100644 index 0000000..7fa2f8b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/res.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_RES_H +#define LEXBOR_CSS_SYNTAX_RES_H + +#define LXB_CSS_SYNTAX_RES_NAME_START 0x01 + +LXB_API const lxb_char_t lxb_css_syntax_res_name_map[256] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01 +}; + +#endif /* LEXBOR_CSS_SYNTAX_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.c new file mode 100644 index 0000000..5b694e9 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.c @@ -0,0 +1,1852 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include +#include + +#include "lexbor/core/utils.h" +#include "lexbor/core/strtod.h" + +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/syntax/tokenizer/error.h" + +#include "lexbor/css/syntax/res.h" + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex[256]; +#endif + +#define LXB_CSS_SYNTAX_ERROR_CODEPOINT 0x1FFFFF + +#define lxb_css_syntax_buffer_append_m(_tkz, _begin, _length) \ + do { \ + if ((_length) > 0) { \ + lxb_status_t status = lxb_css_syntax_string_append(_tkz, \ + (const lxb_char_t *) (_begin), \ + _length); \ + if (status != LXB_STATUS_OK) { \ + return NULL; \ + } \ + } \ + } \ + while (0) + +#define LXB_CSS_SYNTAX_CARRIAGE_RETURN_BLOCK(tkz, begin, data, end) \ + { \ + lxb_css_syntax_buffer_append_m(tkz, begin, ((data) - (begin)) + 1); \ + (tkz)->pos[-1] = 0x0A; \ + \ + /* U+000A LINE FEED (LF) */ \ + if ((data) + 1 < (end) && (data)[1] == 0x0A) { \ + (data) += 1; \ + } \ + \ + (begin) = (data) + 1; \ + } + +#define LXB_CSS_SYNTAX_FORM_FEED_BLOCK(tkz, begin, data, end) \ + { \ + lxb_css_syntax_buffer_append_m(tkz, begin, ((data) - (begin)) + 1); \ + (tkz)->pos[-1] = 0x0A; \ + \ + (begin) = (data) + 1; \ + } + +#define LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) \ + { \ + lxb_css_syntax_buffer_append_m(tkz, begin, ((data) - (begin))); \ + \ + /* U+FFFD REPLACEMENT CHARACTER */ \ + lxb_css_syntax_buffer_append_m(tkz, "\uFFFD", \ + lxb_css_syntax_replacement_length); \ + (begin) = (data) + 1; \ + } + +#define LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) \ + const lxb_char_t *_bad = data; \ + lxb_codepoint_t cp = lxb_css_syntax_state_decode_utf_8_up_80(&data, \ + end); \ + \ + if (cp == LXB_CSS_SYNTAX_ERROR_CODEPOINT) { \ + /* Flush the run before the ill-formed sequence, then U+FFFD. */ \ + lxb_css_syntax_buffer_append_m(tkz, begin, _bad - begin); \ + /* U+FFFD REPLACEMENT CHARACTER */ \ + lxb_css_syntax_buffer_append_m(tkz, "\uFFFD", \ + lxb_css_syntax_replacement_length);\ + begin = data; \ + } + + +static const size_t lxb_css_syntax_replacement_length = 3; /* sizeof("\uFFFD") - 1 */ + + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric_name_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_consume_ident(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_consume_unicode_range(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_bad_url(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +static const lxb_char_t * +lxb_css_syntax_state_escaped(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, const lxb_char_t *end); + +static bool +lxb_css_syntax_state_start_number(const lxb_char_t *data, const lxb_char_t *end); + +static bool +lxb_css_syntax_state_start_ident_sequence(const lxb_char_t *data, + const lxb_char_t *end); + +static lxb_codepoint_t +lxb_css_syntax_state_decode_utf_8_up_80(const lxb_char_t **data, + const lxb_char_t *end); + + +lxb_inline bool +lxb_css_syntax_state_valid_escape(const lxb_char_t *data, + const lxb_char_t *end) +{ + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + data += 1; + if (data >= end || (*data != 0x0A && *data != 0x0C && *data != 0x0D)) { + return true; + } + } + + return false; +} + +lxb_inline bool +lxb_css_syntax_state_non_ascii(lxb_codepoint_t cp) +{ + /* This code is generated by the file utils/lexbor/css/syntax/non_ascii.pl */ + + /* Begin */ + if (cp <= 0x2040) { + if (cp <= 0x00F6) { + if (cp >= 0x00B7 && cp <= 0x00B7) { + return true; + } + else if (cp >= 0x00C0) { + if (cp <= 0x00D6) { + return true; + } + else if (cp >= 0x00D8) { + return true; + } + } + } + else if (cp >= 0x00F8) { + if (cp <= 0x1FFF) { + if (cp <= 0x037D) { + return true; + } + else if (cp >= 0x037F) { + return true; + } + } + else if (cp >= 0x200C) { + if (cp <= 0x200D) { + return true; + } + else if (cp >= 0x203F) { + return true; + } + } + } + } + else if (cp >= 0x2070) { + if (cp <= 0xD7FF) { + if (cp <= 0x218F) { + return true; + } + else if (cp >= 0x2C00) { + if (cp <= 0x2FEF) { + return true; + } + else if (cp >= 0x3001) { + return true; + } + } + } + else if (cp >= 0xF900) { + if (cp <= 0xFFFD) { + if (cp <= 0xFDCF) { + return true; + } + else if (cp >= 0xFDF0) { + return true; + } + } + else if (cp >= 0x10000) { + if (cp <= 0x10FFFF) { + return true; + } + } + } + } + /* End */ + + return false; +} + +lxb_inline bool +lxb_css_syntax_state_start_ident_utf_8_80(const lxb_char_t **data, + const lxb_char_t *end) +{ + lxb_codepoint_t cp; + + cp = lxb_css_syntax_state_decode_utf_8_up_80(data, end); + if (cp == LXB_CSS_SYNTAX_ERROR_CODEPOINT) { + return true; /* To be replaced by \uFFFD. */ + } + + return lxb_css_syntax_state_non_ascii(cp); +} + +lxb_inline lxb_status_t +lxb_css_syntax_string_realloc(lxb_css_syntax_tokenizer_t *tkz, size_t upto) +{ + size_t len = tkz->pos - tkz->start; + size_t size = (tkz->end - tkz->start) + upto; + + lxb_char_t *tmp = lexbor_realloc(tkz->start, size); + if (tmp == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return tkz->status; + } + + tkz->start = tmp; + tkz->pos = tmp + len; + tkz->end = tmp + size; + + return LXB_STATUS_OK; +} + +lxb_inline lxb_status_t +lxb_css_syntax_string_append(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, size_t length) +{ + if ((size_t) (tkz->end - tkz->pos) <= length) { + if (lxb_css_syntax_string_realloc(tkz, length + 1024) != LXB_STATUS_OK) { + return tkz->status; + } + } + + memcpy(tkz->pos, data, length); + + tkz->pos += length; + + return LXB_STATUS_OK; +} + +lxb_inline lxb_status_t +lxb_css_syntax_state_string_term(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz->pos >= tkz->end) { + if (lxb_css_syntax_string_realloc(tkz, 1024) != LXB_STATUS_OK) { + return tkz->status; + } + } + + *tkz->pos = 0x00; + + return LXB_STATUS_OK; +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_state_string_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data) +{ + if(lxb_css_syntax_state_string_term(tkz) != LXB_STATUS_OK) { + return NULL; + } + + lxb_css_syntax_token_string(token)->data = tkz->start; + lxb_css_syntax_token_string(token)->length = tkz->pos - tkz->start; + + tkz->pos = tkz->start; + + return data; +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_state_dimension_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data) +{ + if(lxb_css_syntax_state_string_term(tkz) != LXB_STATUS_OK) { + return NULL; + } + + lxb_css_syntax_token_dimension_string(token)->data = tkz->start; + lxb_css_syntax_token_dimension_string(token)->length = tkz->pos - tkz->start; + + tkz->pos = tkz->start; + + return data; +} + +/* + * Delim + */ +lxb_inline const lxb_char_t * +lxb_css_syntax_state_delim_set(lxb_css_syntax_token_t *token, + const lxb_char_t *data, lxb_char_t ch) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_delim(token)->character = (lxb_codepoint_t) ch; + lxb_css_syntax_token_base(token)->length = 1; + + return data + 1; +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_state_delim_set_cp(lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_codepoint_t cp; + + if (*data < 0x80) { + cp = *data++; + } + else { + cp = lxb_css_syntax_state_decode_utf_8_up_80(&data, end); + } + + token->type = LXB_CSS_SYNTAX_TOKEN_DELIM; + + lxb_css_syntax_token_delim(token)->character = cp; + + return data; +} + +const lxb_char_t * +lxb_css_syntax_state_delim(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_css_syntax_state_delim_set(token, data, *data); + + return data + 1; +} + +/* + * Comment + */ +const lxb_char_t * +lxb_css_syntax_state_comment(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + bool failed; + const lxb_char_t *begin; + + /* Skip forward slash (/) */ + data += 1; + + /* U+002A ASTERISK (*) */ + if (data >= end || *data != 0x2A) { + return lxb_css_syntax_state_delim_set(token, data - 1, '/'); + } + + /* Skip U+002A ASTERISK (*) */ + begin = ++data; + failed = true; + + while (data < end) { + switch (*data) { + /* U+002A ASTERISK (*) */ + case 0x2A: + data += 1; + + if (data >= end) { + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + goto done; + } + + /* U+002F Forward slash (/) */ + if (*data == 0x2F) { + lxb_css_syntax_buffer_append_m(tkz, begin, + (data - begin) - 1); + data += 1; + failed = false; + goto done; + } + + continue; + + /* U+000D CARRIAGE RETURN (CR) */ + case 0x0D: + LXB_CSS_SYNTAX_CARRIAGE_RETURN_BLOCK(tkz, begin, data, end) + break; + + /* U+000C FORM FEED (FF) */ + case 0x0C: + LXB_CSS_SYNTAX_FORM_FEED_BLOCK(tkz, begin, data, end) + break; + + /* U+0000 NULL */ + case 0x00: + LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) + break; + + default: + if (*data >= 0x80) { + LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) + continue; + } + + break; + } + + data += 1; + } + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + +done: + + if (failed) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINCO); + } + + token->type = LXB_CSS_SYNTAX_TOKEN_COMMENT; + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * Whitespace + */ +const lxb_char_t * +lxb_css_syntax_state_whitespace(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + const lxb_char_t *begin; + + begin = data; + + do { + switch (*data) { + /* U+000D CARRIAGE RETURN (CR) */ + case 0x0D: + LXB_CSS_SYNTAX_CARRIAGE_RETURN_BLOCK(tkz, begin, data, end) + break; + + /* U+000C FORM FEED (FF) */ + case 0x0C: + LXB_CSS_SYNTAX_FORM_FEED_BLOCK(tkz, begin, data, end) + break; + + case 0x09: + case 0x20: + case 0x0A: + break; + + default: + goto done; + } + + data += 1; + } + while (data < end); + +done: + + token->type = LXB_CSS_SYNTAX_TOKEN_WHITESPACE; + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * String token for U+0022 Quotation Mark (") and U+0027 Apostrophe (') + */ +const lxb_char_t * +lxb_css_syntax_state_string(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_char_t mark; + const lxb_char_t *begin; + lxb_codepoint_t cp; + + mark = *data++; + begin = data; + + token->type = LXB_CSS_SYNTAX_TOKEN_STRING; + + while (data < end) { + switch (*data) { + /* U+0000 NULL */ + case 0x00: + LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) + break; + + /* + * U+000A LINE FEED + * U+000D CARRIAGE RETURN + * U+000C FORM FEED + */ + case 0x0A: + case 0x0D: + case 0x0C: + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_NEINST); + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_STRING; + + goto done; + + /* U+005C REVERSE SOLIDUS (\) */ + case 0x5C: + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + data += 1; + begin = data; + + if (data >= end) { + data = lxb_css_syntax_state_escaped(tkz, data, end); + begin = data; + continue; + } + + switch (*data) { + /* U+000D CARRIAGE RETURN (CR) */ + case 0x0D: + /* U+000A LINE FEED (LF) */ + if (data + 1 < end && data[1] == 0x0A) { + data += 1; + } + + begin = data + 1; + break; + + /* U+000C FORM FEED (FF) */ + case 0x0A: + case 0x0C: + begin = data + 1; + break; + + /* U+0000 NULL */ + case 0x00: + LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) + break; + + default: + if (*data < 0x80) { + data = lxb_css_syntax_state_escaped(tkz, data, end); + begin = data; + } + else { + cp = lxb_css_syntax_state_decode_utf_8_up_80(&data, end); + + if (cp == LXB_CSS_SYNTAX_ERROR_CODEPOINT) { + /* U+FFFD REPLACEMENT CHARACTER */ + lxb_css_syntax_buffer_append_m(tkz, "\uFFFD", + lxb_css_syntax_replacement_length); + begin = data; + } + } + + continue; + } + + break; + + default: + /* '"' or '\'' */ + if (*data == mark) { + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + return lxb_css_syntax_state_string_set(tkz, token, data + 1); + } + + if (*data >= 0x80) { + LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) + continue; + } + + break; + } + + data += 1; + } + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINST); +done: + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +/* + * U+0023 NUMBER SIGN (#) + */ +const lxb_char_t * +lxb_css_syntax_state_hash(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, const lxb_char_t *data, + const lxb_char_t *end) +{ + lxb_char_t ch; + const lxb_char_t *begin; + + data += 1; + if (data >= end) { + return lxb_css_syntax_state_delim_set(token, data - 1, '#'); + } + + if (*data >= 0x80) { + begin = data; + + if (lxb_css_syntax_state_start_ident_utf_8_80(&data, end)) { + data = begin; + goto hash; + } + + return lxb_css_syntax_state_delim_set(token, begin - 1, '#'); + } + + if (lxb_css_syntax_res_name_map[*data] == 0x00) { + if (*data == 0x00) { + goto hash; + } + + /* U+005C REVERSE SOLIDUS (\) */ + if (*data != 0x5C) { + if (*data >= 0x80) { + begin = data; + + if (lxb_css_syntax_state_start_ident_utf_8_80(&data, end)) { + data = begin; + goto hash; + } + } + + return lxb_css_syntax_state_delim_set(token, data - 1, '#'); + } + + data += 1; + + if (data < end) { + ch = *data; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + return lxb_css_syntax_state_delim_set(token, data - 2, '#'); + } + } + + data -= 1; + } + +hash: + + token->type = LXB_CSS_SYNTAX_TOKEN_HASH; + + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); +} + +/* + * U+0028 LEFT PARENTHESIS (() + */ +const lxb_char_t * +lxb_css_syntax_state_lparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS; + return data + 1; +} + +/* + * U+0029 RIGHT PARENTHESIS ()) + */ +const lxb_char_t * +lxb_css_syntax_state_rparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + return data + 1; +} + +/* + * U+002B PLUS SIGN (+) + */ +const lxb_char_t * +lxb_css_syntax_state_plus(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + /* Skip U+002B PLUS SIGN (+). */ + data += 1; + + if (data >= end) { + return lxb_css_syntax_state_delim_set(token, data - 1, '+'); + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_number(token)->have_sign = true; + return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); + } + + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + data += 1; + + if (data >= end) { + return lxb_css_syntax_state_delim_set(token, data - 2, '+'); + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + lxb_css_syntax_token_number(token)->have_sign = true; + return lxb_css_syntax_state_consume_numeric(tkz, token, + data - 1, end); + } + + data -= 1; + } + + return lxb_css_syntax_state_delim_set(token, data - 1, '+'); +} + +/* + * U+002C COMMA (,) + */ +const lxb_char_t * +lxb_css_syntax_state_comma(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_COMMA; + return data + 1; +} + +/* + * U+002D HYPHEN-MINUS (-) + */ +const lxb_char_t * +lxb_css_syntax_state_minus(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_css_syntax_token_number_t *number; + + /* Check for */ + + if (lxb_css_syntax_state_start_number(data, end)) { + data = lxb_css_syntax_state_consume_numeric(tkz, token, data + 1, end); + + number = lxb_css_syntax_token_number(token); + number->num = -number->num; + + lxb_css_syntax_token_number(token)->have_sign = true; + + return data; + } + + /* Check for */ + + if (data + 3 <= end) { + /* U+002D HYPHEN-MINUS U+003E GREATER-THAN SIGN (->) */ + if (data[1] == 0x2D && data[2] == 0x3E) { + token->type = LXB_CSS_SYNTAX_TOKEN_CDC; + return data + 3; + } + } + + if (lxb_css_syntax_state_start_ident_sequence(data, end)) { + return lxb_css_syntax_state_ident_like_not_url(tkz, token, + data, end); + } + + return lxb_css_syntax_state_delim_set(token, data, '-'); +} + +/* + * U+002E FULL STOP (.) + */ +const lxb_char_t * +lxb_css_syntax_state_full_stop(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + if (lxb_css_syntax_state_start_number(data, end)) { + lxb_css_syntax_token_number(token)->have_sign = false; + return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); + } + + return lxb_css_syntax_state_delim_set(token, data, '.'); +} + +/* + * U+003A COLON (:) + */ +const lxb_char_t * +lxb_css_syntax_state_colon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_COLON; + return data + 1; +} + +/* + * U+003B SEMICOLON (;) + */ +const lxb_char_t * +lxb_css_syntax_state_semicolon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_SEMICOLON; + return data + 1; +} + +/* + * U+003C LESS-THAN SIGN (<) + */ +const lxb_char_t * +lxb_css_syntax_state_less_sign(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + if (data + 4 <= end) { + /* U+0021 EXCLAMATION MARK + * U+002D HYPHEN-MINUS + * U+002D HYPHEN-MINUS + * (!--) + */ + if (data[1] == 0x21 && data[2] == 0x2D && data[3] == 0x2D) { + token->type = LXB_CSS_SYNTAX_TOKEN_CDO; + return data + 4; + } + } + + return lxb_css_syntax_state_delim_set(token, data, '<'); +} + +/* + * U+0040 COMMERCIAL AT (@) + */ +const lxb_char_t * +lxb_css_syntax_state_at(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + /* Skip U+0040 COMMERCIAL AT (@) */ + data += 1; + + if (lxb_css_syntax_state_start_ident_sequence(data, end)) { + token->type = LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD; + + return lxb_css_syntax_state_consume_ident(tkz, token, data, end); + } + + return lxb_css_syntax_state_delim_set(token, data - 1, '@'); +} + +/* + * U+005B LEFT SQUARE BRACKET ([) + */ +const lxb_char_t * +lxb_css_syntax_state_ls_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_LS_BRACKET; + return data + 1; +} + +/* + * U+005C REVERSE SOLIDUS (\) + */ +const lxb_char_t * +lxb_css_syntax_state_rsolidus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + if (lxb_css_syntax_state_valid_escape(data, end)) { + return lxb_css_syntax_state_ident_like(tkz, token, data, end); + } + + return lxb_css_syntax_state_delim_set(token, data, '\\'); +} + +/* + * U+005D RIGHT SQUARE BRACKET (]) + */ +const lxb_char_t * +lxb_css_syntax_state_rs_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + return data + 1; +} + +/* + * U+007B LEFT CURLY BRACKET ({) + */ +const lxb_char_t * +lxb_css_syntax_state_lc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_LC_BRACKET; + return data + 1; +} + +/* + * U+007D RIGHT CURLY BRACKET (}) + */ +const lxb_char_t * +lxb_css_syntax_state_rc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + token->type = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + return data + 1; +} + +/* + * Numeric + */ +lxb_inline void +lxb_css_syntax_consume_numeric_set(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *start, const lxb_char_t *end, + bool is_float, bool e_is_negative, + int exponent, int e_digit) +{ + if (e_is_negative) { + exponent = e_digit - exponent; + exponent = -exponent; + } + else { + exponent = e_digit + exponent; + } + + double num = lexbor_strtod_internal(start, (end - start), exponent); + + token->type = LXB_CSS_SYNTAX_TOKEN_NUMBER; + + lxb_css_syntax_token_number(token)->is_float = is_float; + lxb_css_syntax_token_number(token)->num = num; +} + +const lxb_char_t * +lxb_css_syntax_state_consume_before_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + lxb_css_syntax_token_number(token)->have_sign = false; + + return lxb_css_syntax_state_consume_numeric(tkz, token, data, end); +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + bool e_is_negative, is_float; + int exponent, e_digit; + lxb_char_t ch, *buf_p; + const lxb_char_t *begin, *buf_end; + lxb_css_syntax_token_t *t_str; + lxb_css_syntax_token_string_t *str; + lxb_char_t buf[128]; + + buf_p = buf; + buf_end = buf + sizeof(buf); + + str = lxb_css_syntax_token_dimension_string(token); + t_str = (lxb_css_syntax_token_t *) (void *) str; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + while (*data >= 0x30 && *data <= 0x39) { + if (buf_p != buf_end) { + *buf_p++ = *data; + } + + data += 1; + + if (data >= end) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + false, false, 0, 0); + return data; + } + } + + exponent = 0; + is_float = false; + + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + data += 1; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (data >= end || *data < 0x30 || *data > 0x39) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + false, false, 0, 0); + return data - 1; + } + + begin = buf_p; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + do { + if (buf_p != buf_end) { + *buf_p++ = *data; + } + + data += 1; + } + while (data < end && *data >= 0x30 && *data <= 0x39); + + exponent = -(int) (buf_p - begin); + is_float = true; + + if (data >= end) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, false, exponent, 0); + return data; + } + } + + ch = *data; + + /* U+0045 Latin Capital Letter (E) or U+0065 Latin Small Letter (e) */ + if (ch != 0x45 && ch != 0x65) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); + + return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, + data, end); + } + + lxb_css_syntax_token_base(t_str)->begin = data; + + data += 1; + + if (data >= end) { + data -= 1; + + lxb_css_syntax_token_base(t_str)->length = 1; + lxb_css_syntax_buffer_append_m(tkz, data, 1); + + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + return lxb_css_syntax_state_dimension_set(tkz, token, data + 1); + } + + e_is_negative = false; + + switch (*data) { + /* U+002D HYPHEN-MINUS (-) */ + case 0x2D: + e_is_negative = true; + /* fall through */ + + /* U+002B PLUS SIGN (+) */ + case 0x2B: + data += 1; + break; + + default: + break; + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (data >= end || *data < 0x30 || *data > 0x39) { + data -= 1; + if (*data != ch) { + data -= 1; + } + + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + is_float, false, exponent, 0); + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + begin = lxb_css_syntax_state_consume_ident(tkz, t_str, data, end); + + lxb_css_syntax_token_base(t_str)->begin = data; + lxb_css_syntax_token_base(t_str)->length = begin - data; + + return begin; + } + + e_digit = 0; + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + do { + if (e_digit < INT_MAX / 10) { + e_digit = (*data - 0x30) + e_digit * 0x0A; + } + + data += 1; + + if (data >= end) { + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, e_is_negative, + exponent, e_digit); + return data; + } + } + while(*data >= 0x30 && *data <= 0x39); + + lxb_css_syntax_consume_numeric_set(tkz, token, buf, buf_p, + true, e_is_negative, + exponent, e_digit); + + return lxb_css_syntax_state_consume_numeric_name_start(tkz, token, + data, end); +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_numeric_name_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + const lxb_char_t *p; + lxb_css_syntax_token_t *t_str; + lxb_css_syntax_token_string_t *str; + + if (lxb_css_syntax_state_start_ident_sequence(data, end)) { + str = lxb_css_syntax_token_dimension_string(token); + t_str = (lxb_css_syntax_token_t *) (void *) str; + + token->type = LXB_CSS_SYNTAX_TOKEN_DIMENSION; + + p = lxb_css_syntax_state_consume_ident(tkz, t_str, data, end); + + lxb_css_syntax_token_base(t_str)->begin = data; + lxb_css_syntax_token_base(t_str)->length = p - data; + + return p; + } + + /* U+0025 PERCENTAGE SIGN (%) */ + if (*data == 0x25) { + token->type = LXB_CSS_SYNTAX_TOKEN_PERCENTAGE; + return data + 1; + } + + return data; +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_ident(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + const lxb_char_t *begin; + + begin = data; + + while (data < end) { + if (*data < 0x80) { + if (lxb_css_syntax_res_name_map[*data] == 0x00) { + /* U+005C REVERSE SOLIDUS (\) */ + if (*data == 0x5C) { + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + data += 1; + + if (data < end) { + if (*data == 0x0A || *data == 0x0C || *data == 0x0D) { + data -= 1; + begin = data; + break; + } + } + + data = lxb_css_syntax_state_escaped(tkz, data, end); + if (data == NULL) { + return NULL; + } + + begin = data; + continue; + } + else if (*data == 0x00) + LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) + else { + break; + } + } + + data += 1; + } + else { + LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) + else if (!lxb_css_syntax_state_non_ascii(cp)) { + data = _bad; + break; + } + } + } + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +static const lxb_char_t * +lxb_css_syntax_state_consume_unicode_range(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, + const lxb_char_t *end) +{ + bool mq; + unsigned count, question; + lxb_codepoint_t cp, range_start, range_end; + + token->type = LXB_CSS_SYNTAX_TOKEN_UNICODE_RANGE; + + /* Skip [Uu]\+ */ + data += 2; + mq = true; + +again: + + cp = 0x0000; + count = 6; + + while (data < end && count > 0) { + if (lexbor_str_res_map_hex[*data] == LEXBOR_STR_RES_SLIP) { + /* U+003F QUESTION MARK (?) */ + if (*data == 0x3F && mq) { + question = 0; + + do { + question += 1; + count -= 1; + data += 1; + } + while (data < end && *data == 0x3F && count > 0); + + range_start = cp << (4 * question); + range_end = range_start | ((1 << (4 * question)) - 1); + + lxb_css_syntax_token_unicode_range(token)->start = range_start; + lxb_css_syntax_token_unicode_range(token)->end = range_end; + + return data; + } + + break; + } + + cp <<= 4; + cp |= lexbor_str_res_map_hex[ *data ]; + + count -= 1; + data += 1; + } + + if (mq) { + mq = false; + + lxb_css_syntax_token_unicode_range(token)->start = cp; + + /* U+002D HYPHEN-MINUS (-) */ + if (data + 2 > end || data[0] != 0x2D + || lexbor_str_res_map_hex[data[1]] == LEXBOR_STR_RES_SLIP) + { + lxb_css_syntax_token_unicode_range(token)->end = cp; + return data; + } + + /* Skip U+002D HYPHEN-MINUS (-) */ + data += 1; + + goto again; + } + + lxb_css_syntax_token_unicode_range(token)->end = cp; + + return data; +} + + +const lxb_char_t * +lxb_css_syntax_state_ident_like(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_char_t ch; + const lxb_char_t *begin; + lxb_css_syntax_token_string_t *str; + static const lxb_char_t url[] = "url"; + + /* Would start a unicode-range. */ + /* + * U+002B PLUS SIGN (+) + * U+003F QUESTION MARK (?) + */ + if (tkz->with_unicode_range && data + 3 <= end && data[1] == 0x2B + && (lexbor_str_res_map_hex[ data[2] ] != 0xFF || data[2] == 0x3F)) + { + return lxb_css_syntax_state_consume_unicode_range(tkz, token, + data, end); + } + + data = lxb_css_syntax_state_consume_ident(tkz, token, data, end); + if (data == NULL) { + goto done; + } + + if (data < end && *data == '(') { + data += 1; + str = lxb_css_syntax_token_string(token); + + if (str->length != 3 || !lexbor_str_data_casecmp(str->data, url)) { + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + return data; + } + + begin = data; + + while (data < end) { + ch = *data; + + if (lexbor_utils_whitespace(ch, !=, &&)) { + /* U+0022 QUOTATION MARK (") or U+0027 APOSTROPHE (') */ + if (ch == 0x22 || ch == 0x27) { + break; + } + + tkz->pos = tkz->start; + + return lxb_css_syntax_state_url(tkz, token, data, end); + } + + data++; + } + + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + return begin; + } + +done: + + token->type = LXB_CSS_SYNTAX_TOKEN_IDENT; + + return data; +} + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + const lxb_char_t *begin = data; + + if (lxb_css_syntax_state_start_ident_utf_8_80(&data, end)) { + return lxb_css_syntax_state_ident_like_not_url(tkz, token, begin, end); + } + + return lxb_css_syntax_state_delim_set_cp(token, begin, end); +} + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + data = lxb_css_syntax_state_consume_ident(tkz, token, data, end); + if (data == NULL) { + return NULL; + } + + if (data < end && *data == '(') { + token->type = LXB_CSS_SYNTAX_TOKEN_FUNCTION; + + return data + 1; + } + + token->type = LXB_CSS_SYNTAX_TOKEN_IDENT; + + return data; +} + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url_surrogate(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + const lxb_char_t *begin = data; + + if (data + 3 <= end) { + lxb_codepoint_t srp = data[1] << 8 | data[2]; + + /* Leading surrogate U+D800 to U+DFFF. */ + if (srp >= (0xA0 << 8 | 0x80) && srp <= (0xBF << 8 | 0xBF)) { + return lxb_css_syntax_state_ident_like_not_url(tkz, token, + data, end); + } + } + + if (lxb_css_syntax_state_start_ident_utf_8_80(&data, end)) { + return lxb_css_syntax_state_ident_like_not_url(tkz, token, begin, end); + } + + return lxb_css_syntax_state_delim_set_cp(token, begin, end); +} + +/* + * URL + */ +static const lxb_char_t * +lxb_css_syntax_state_url(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + lxb_char_t ch; + const lxb_char_t *begin; + + *tkz->pos = 0x00; + + begin = data; + + while (data < end) { + switch (*data) { + /* U+0000 NULL */ + case 0x00: + LXB_CSS_SYNTAX_NULL_BLOCK(tkz, begin, data, end) + break; + + /* U+0029 RIGHT PARENTHESIS ()) */ + case 0x29: + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + data += 1; + goto done; + + /* + * U+0022 QUOTATION MARK (") + * U+0027 APOSTROPHE (') + * U+0028 LEFT PARENTHESIS (() + * U+000B LINE TABULATION + * U+007F DELETE + */ + case 0x22: + case 0x27: + case 0x28: + case 0x0B: + case 0x7F: + goto bad; + + /* U+005C REVERSE SOLIDUS (\) */ + case 0x5C: + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + data += 1; + + if (data >= end) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_WRESINUR); + } + else { + ch = *data; + + if (ch == 0x0A || ch == 0x0C || ch == 0x0D) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_WRESINUR); + + return lxb_css_syntax_state_bad_url(tkz, token, data, end); + } + } + + data = lxb_css_syntax_state_escaped(tkz, data, end); + if (data == NULL) { + return NULL; + } + + begin = data; + continue; + + /* + * U+0009 CHARACTER TABULATION (tab) + * U+000A LINE FEED (LF) + * U+000C FORM FEED (FF) + * U+000D CARRIAGE RETURN (CR) + * U+0020 SPACE + */ + case 0x09: + case 0x0A: + case 0x0C: + case 0x0D: + case 0x20: + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + data += 1; + + while (data < end) { + ch = *data; + + if (lexbor_utils_whitespace(ch, !=, &&)) { + /* U+0029 RIGHT PARENTHESIS ()) */ + if (*data == 0x29) { + data += 1; + goto done; + } + + return lxb_css_syntax_state_bad_url(tkz, token, + data, end); + } + + data += 1; + } + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINUR); + + goto done; + + default: + if (*data >= 0x80) { + LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) + continue; + } + + /* + * Inclusive: + * U+0000 NULL and U+0008 BACKSPACE or + * U+000E SHIFT OUT and U+001F INFORMATION SEPARATOR ONE + */ + if ((*data <= 0x08) || (*data >= 0x0E && *data <= 0x1F)) { + goto bad; + } + + break; + } + + data += 1; + } + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINUR); + +done: + + token->type = LXB_CSS_SYNTAX_TOKEN_URL; + + return lxb_css_syntax_state_string_set(tkz, token, data); + +bad: + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_QOINUR); + + return lxb_css_syntax_state_bad_url(tkz, token, data, end); +} + +/* + * Bad URL + */ +static const lxb_char_t * +lxb_css_syntax_state_bad_url(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end) +{ + const lxb_char_t *begin; + + token->type = LXB_CSS_SYNTAX_TOKEN_BAD_URL; + + begin = data; + + while (data < end) { + /* U+0029 RIGHT PARENTHESIS ()) */ + if (*data == 0x29) { + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + return lxb_css_syntax_state_string_set(tkz, token, data + 1); + } + /* U+005C REVERSE SOLIDUS (\) */ + else if (*data == 0x5C) { + data += 1; + if (data >= end) { + break; + } + } + + if (*data >= 0x80) { + LXB_CSS_SYNTAX_UTF_8_UP_80_BLOCK(tkz, begin, data, end) + continue; + } + + data += 1; + } + + lxb_css_syntax_buffer_append_m(tkz, begin, data - begin); + + return lxb_css_syntax_state_string_set(tkz, token, data); +} + +static const lxb_char_t * +lxb_css_syntax_state_escaped(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, const lxb_char_t *end) +{ + unsigned count; + lxb_codepoint_t cp; + + cp = 0; + + for (count = 0; count < 6; count++, data++) { + if (data >= end) { + if (count == 0) { + lxb_css_syntax_tokenizer_error_add(tkz->parse_errors, data, + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINES); + } + + goto done; + } + + if (lexbor_str_res_map_hex[*data] == 0xFF) { + if (count == 0) { + if (*data < 0x80) { + cp = (lxb_codepoint_t) *data++; + } + else { + cp = lxb_css_syntax_state_decode_utf_8_up_80(&data, end); + } + + goto done; + } + + break; + } + + cp <<= 4; + cp |= lexbor_str_res_map_hex[*data]; + } + + if (data < end) { + switch (*data) { + case 0x0D: + data += 1; + + if (data >= end) { + break; + } + + if (*data == 0x0A) { + data += 1; + } + + break; + + case 0x09: + case 0x20: + case 0x0A: + case 0x0C: + data += 1; + break; + + default: + break; + } + } + +done: + + if ((tkz->end - tkz->pos) < 6) { + if (lxb_css_syntax_string_realloc(tkz, 1024) != LXB_STATUS_OK) { + return NULL; + } + } + + lxb_css_syntax_codepoint_to_ascii(tkz, cp); + + return data; +} + +static bool +lxb_css_syntax_state_start_number(const lxb_char_t *data, const lxb_char_t *end) +{ + switch (*data) { + /* + * U+002B PLUS SIGN (+) + * U+002D HYPHEN-MINUS (-) + */ + case 0x2B: + case 0x2D: + data += 1; + if (data >= end) { + return false; + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + return true; + } + + break; + + default: + break; + } + + /* U+0030 DIGIT ZERO (0) and U+0039 DIGIT NINE (9) */ + if (*data >= 0x30 && *data <= 0x39) { + return true; + } + + /* U+002E FULL STOP (.) */ + if (*data == 0x2E) { + data += 1; + + if (data >= end || *data < 0x30 || *data > 0x39) { + return false; + } + + return true; + } + + return false; +} + +static bool +lxb_css_syntax_state_start_ident_sequence(const lxb_char_t *data, + const lxb_char_t *end) +{ + if (data >= end) { + return false; + } + + if (*data < 0x80) { + /* U+002D HYPHEN-MINUS */ + if (*data == 0x2D) { + data += 1; + + if (data >= end) { + return false; + } + + if (*data < 0x80) { + /* U+002D HYPHEN-MINUS */ + if (*data == 0x2D + || lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START + || lxb_css_syntax_state_valid_escape(data, end) + || *data == 0x00) + { + return true; + } + } + } + /* U+005C REVERSE SOLIDUS (\) */ + else if (*data == 0x5C) { + return lxb_css_syntax_state_valid_escape(data, end); + } + + return lxb_css_syntax_res_name_map[*data] == LXB_CSS_SYNTAX_RES_NAME_START + || *data == 0x00; + } + + return lxb_css_syntax_state_start_ident_utf_8_80(&data, end); +} + +static lxb_codepoint_t +lxb_css_syntax_state_decode_utf_8_up_80(const lxb_char_t **data, + const lxb_char_t *end) +{ + lxb_char_t ch, lower, upper; + unsigned needed; + const lxb_char_t *p; + lxb_codepoint_t cp; + + /* + * WHATWG UTF-8 decoder: https://encoding.spec.whatwg.org/#utf-8-decoder + */ + + p = *data; + ch = *p++; + + if (ch <= 0xDF) { + if (ch < 0xC2) { + *data = p; + return LXB_CSS_SYNTAX_ERROR_CODEPOINT; + } + + needed = 1; + lower = 0x80; + upper = 0xBF; + cp = ch & 0x1F; + } + else if (ch <= 0xEF) { + needed = 2; + lower = (ch == 0xE0) ? 0xA0 : 0x80; + upper = (ch == 0xED) ? 0x9F : 0xBF; + cp = ch & 0x0F; + } + else if (ch <= 0xF4) { + needed = 3; + lower = (ch == 0xF0) ? 0x90 : 0x80; + upper = (ch == 0xF4) ? 0x8F : 0xBF; + cp = ch & 0x07; + } + else { + /* 0xF5..0xFF: an invalid lead byte. */ + *data = p; + return LXB_CSS_SYNTAX_ERROR_CODEPOINT; + } + + do { + if (p >= end || *p < lower || *p > upper) { + *data = p; + return LXB_CSS_SYNTAX_ERROR_CODEPOINT; + } + + cp = (cp << 6) | (*p++ & 0x3F); + + lower = 0x80; + upper = 0xBF; + } + while (--needed != 0); + + *data = p; + + return cp; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.h new file mode 100644 index 0000000..59b5a87 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_STATE_H +#define LEXBOR_CSS_SYNTAX_STATE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/syntax/base.h" +#include "lexbor/css/syntax/tokenizer.h" + + +const lxb_char_t * +lxb_css_syntax_state_delim(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_comment(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_whitespace(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_string(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_hash(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_lparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_rparenthesis(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_plus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_comma(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_minus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_full_stop(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_colon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_semicolon(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_less_sign(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_at(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_ls_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_rsolidus(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_rs_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_lc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_rc_bracket(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_consume_before_numeric(lxb_css_syntax_tokenizer_t *tkz, lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_ident_like(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url_start(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +const lxb_char_t * +lxb_css_syntax_state_ident_like_not_url_surrogate(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_STATE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state_res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state_res.h new file mode 100644 index 0000000..e98909a --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/state_res.h @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_STATE_RES_H +#define LEXBOR_CSS_SYNTAX_STATE_RES_H + + +static const lxb_css_syntax_tokenizer_state_f +lxb_css_syntax_state_res_map[256] = +{ + lxb_css_syntax_state_ident_like_not_url, /* 0x00; 'NUL'; NULL */ + lxb_css_syntax_state_delim, /* 0x01; 'SOH'; Start of Heading */ + lxb_css_syntax_state_delim, /* 0x02; 'STX'; Start of text */ + lxb_css_syntax_state_delim, /* 0x03; 'ETX'; End of text */ + lxb_css_syntax_state_delim, /* 0x04; 'EOT'; End of Transmission */ + lxb_css_syntax_state_delim, /* 0x05; 'ENQ'; Enquiry */ + lxb_css_syntax_state_delim, /* 0x06; 'ACK'; Acknowledge */ + lxb_css_syntax_state_delim, /* 0x07; 'BEL'; Bell */ + lxb_css_syntax_state_delim, /* 0x08; 'BS'; Backspace */ + lxb_css_syntax_state_whitespace, /* 0x09; 'TAB'; Horizontal Tab */ + lxb_css_syntax_state_whitespace, /* 0x0A; 'LF'; Line Feed ('\n') */ + lxb_css_syntax_state_delim, /* 0x0B; 'VT'; Vertical Tab */ + lxb_css_syntax_state_whitespace, /* 0x0C; 'FF'; Form Feed */ + lxb_css_syntax_state_whitespace, /* 0x0D; 'CR'; Carriage Return ('\r') */ + lxb_css_syntax_state_delim, /* 0x0E; 'SO'; Shift Out */ + lxb_css_syntax_state_delim, /* 0x0F; 'SI'; Shift In */ + lxb_css_syntax_state_delim, /* 0x10; 'DLE'; Data Link Escape */ + lxb_css_syntax_state_delim, /* 0x11; 'DC1'; Device Control #1 */ + lxb_css_syntax_state_delim, /* 0x12; 'DC2'; Device Control #2 */ + lxb_css_syntax_state_delim, /* 0x13; 'DC3'; Device Control #3 */ + lxb_css_syntax_state_delim, /* 0x14; 'DC4'; Device Control #4 */ + lxb_css_syntax_state_delim, /* 0x15; 'NAK'; Negative Acknowledge */ + lxb_css_syntax_state_delim, /* 0x16; 'SYN'; Synchronous Idle */ + lxb_css_syntax_state_delim, /* 0x17; 'ETB'; End of Transmission Block */ + lxb_css_syntax_state_delim, /* 0x18; 'CAN'; Cancel */ + lxb_css_syntax_state_delim, /* 0x19; 'EM'; End of Medium */ + lxb_css_syntax_state_delim, /* 0x1A; 'SUB'; Substitute */ + lxb_css_syntax_state_delim, /* 0x1B; 'ESC'; Escape */ + lxb_css_syntax_state_delim, /* 0x1C; 'FS'; File Separator */ + lxb_css_syntax_state_delim, /* 0x1D; 'GS'; Group Separator */ + lxb_css_syntax_state_delim, /* 0x1E; 'RS'; Record Separator */ + lxb_css_syntax_state_delim, /* 0x1F; 'US'; Unit Separator */ + lxb_css_syntax_state_whitespace, /* 0x20; 'SP'; Space */ + lxb_css_syntax_state_delim, /* 0x21; '!'; Exclamation mark */ + lxb_css_syntax_state_string, /* 0x22; '"'; Only quotes above */ + lxb_css_syntax_state_hash, /* 0x23; '#'; Pound sign */ + lxb_css_syntax_state_delim, /* 0x24; '$'; Dollar sign */ + lxb_css_syntax_state_delim, /* 0x25; '%'; Percentage sign */ + lxb_css_syntax_state_delim, /* 0x26; '&'; Ampersand */ + lxb_css_syntax_state_string, /* 0x27; '''; Apostrophe */ + lxb_css_syntax_state_lparenthesis, /* 0x28; '('; Left bracket */ + lxb_css_syntax_state_rparenthesis, /* 0x29; ')'; Right bracket */ + lxb_css_syntax_state_delim, /* 0x2A; '*'; Asterisk */ + lxb_css_syntax_state_plus, /* 0x2B; '+'; Plus symbol */ + lxb_css_syntax_state_comma, /* 0x2C; ','; Comma */ + lxb_css_syntax_state_minus, /* 0x2D; '-'; Dash */ + lxb_css_syntax_state_full_stop, /* 0x2E; '.'; Full stop */ + lxb_css_syntax_state_comment, /* 0x2F; '/'; Forward slash */ + lxb_css_syntax_state_consume_before_numeric, /* 0x30; '0' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x31; '1' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x32; '2' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x33; '3' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x34; '4' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x35; '5' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x36; '6' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x37; '7' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x38; '8' */ + lxb_css_syntax_state_consume_before_numeric, /* 0x39; '9' */ + lxb_css_syntax_state_colon, /* 0x3A; ':'; Colon */ + lxb_css_syntax_state_semicolon, /* 0x3B; ';'; Semicolon */ + lxb_css_syntax_state_less_sign, /* 0x3C; '<'; Small than bracket */ + lxb_css_syntax_state_delim, /* 0x3D; '='; Equals sign */ + lxb_css_syntax_state_delim, /* 0x3E; '>'; Bigger than symbol */ + lxb_css_syntax_state_delim, /* 0x3F; '?'; Question mark */ + lxb_css_syntax_state_at, /* 0x40; '@'; At symbol */ + lxb_css_syntax_state_ident_like_not_url, /* 0x41; 'A' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x42; 'B' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x43; 'C' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x44; 'D' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x45; 'E' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x46; 'F' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x47; 'G' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x48; 'H' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x49; 'I' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4A; 'J' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4B; 'K' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4C; 'L' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4D; 'M' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4E; 'N' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x4F; 'O' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x50; 'P' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x51; 'Q' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x52; 'R' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x53; 'S' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x54; 'T' */ + lxb_css_syntax_state_ident_like, /* 0x55; 'U' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x56; 'V' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x57; 'W' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x58; 'X' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x59; 'Y' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x5A; 'Z' */ + lxb_css_syntax_state_ls_bracket, /* 0x5B; '['; Left square bracket */ + lxb_css_syntax_state_rsolidus, /* 0x5C; '\'; Inverse/backward slash */ + lxb_css_syntax_state_rs_bracket, /* 0x5D; ']'; Right square bracket */ + lxb_css_syntax_state_delim, /* 0x5E; '^'; Circumflex */ + lxb_css_syntax_state_ident_like_not_url, /* 0x5F; '_'; Underscore */ + lxb_css_syntax_state_delim, /* 0x60; '`'; Gravis (backtick) */ + lxb_css_syntax_state_ident_like_not_url, /* 0x61; 'a' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x62; 'b' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x63; 'c' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x64; 'd' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x65; 'e' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x66; 'f' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x67; 'g' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x68; 'h' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x69; 'i' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6A; 'j' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6B; 'k' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6C; 'l' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6D; 'm' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6E; 'n' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x6F; 'o' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x70; 'p' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x71; 'q' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x72; 'r' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x73; 's' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x74; 't' */ + lxb_css_syntax_state_ident_like, /* 0x75; 'u' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x76; 'v' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x77; 'w' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x78; 'x' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x79; 'y' */ + lxb_css_syntax_state_ident_like_not_url, /* 0x7A; 'z' */ + lxb_css_syntax_state_lc_bracket, /* 0x7B; '{'; Left curly bracket */ + lxb_css_syntax_state_delim, /* 0x7C; '|'; Vertical line */ + lxb_css_syntax_state_rc_bracket, /* 0x7D; '}'; Right curly brackets */ + lxb_css_syntax_state_delim, /* 0x7E; '~'; Tilde */ + lxb_css_syntax_state_delim, /* 0x7F; 'DEL'; Delete */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x80 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x81 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x82 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x83 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x84 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x85 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x86 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x87 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x88 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x89 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8A */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8B */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8C */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8D */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8E */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x8F */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x90 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x91 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x92 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x93 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x94 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x95 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x96 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x97 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x98 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x99 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9A */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9B */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9C */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9D */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9E */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0x9F */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xA9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAC */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAD */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAE */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xAF */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xB9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBC */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBD */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBE */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xBF */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xC9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCC */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCD */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCE */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xCF */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xD9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDC */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDD */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDE */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xDF */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xE9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xEA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xEB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xEC */ + lxb_css_syntax_state_ident_like_not_url_surrogate, /* 0xED */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xEE */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xEF */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF0 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF1 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF2 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF3 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF4 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF5 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF6 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF7 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF8 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xF9 */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xFA */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xFB */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xFC */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xFD */ + lxb_css_syntax_state_ident_like_not_url_start, /* 0xFE */ + lxb_css_syntax_state_ident_like_not_url_start /* 0xFF */ +}; + + +#endif /* LEXBOR_CSS_SYNTAX_STATE_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.c new file mode 100644 index 0000000..fd03fe0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.c @@ -0,0 +1,537 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/syntax.h" +#include "lexbor/css/parser.h" +#include "lexbor/css/rule.h" + +#include "lexbor/core/serialize.h" +#include "lexbor/core/str.h" + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_ansi_replacement_character[4]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex[256]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex_to_char_lowercase[17]; + LXB_EXTERN const char *lexbor_str_res_char_to_two_hex_value_lowercase[257]; + LXB_EXTERN const lxb_char_t lxb_css_syntax_res_name_map[256]; +#endif + +static const lexbor_str_t lxb_css_syntax_str_ws = lexbor_str(" "); + + +lxb_css_rule_list_t * +lxb_css_syntax_parse_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *cb, + const lxb_char_t *data, size_t length) +{ + lxb_css_rule_list_t *list; + lxb_css_syntax_rule_t *rule; + + if (lxb_css_parser_is_running(parser)) { + parser->status = LXB_STATUS_ERROR_WRONG_STAGE; + return NULL; + } + + lxb_css_parser_clean(parser); + lxb_css_parser_buffer_set(parser, data, length); + + list = lxb_css_rule_list_create(parser->memory); + if (list == NULL) { + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + goto failed; + } + + rule = lxb_css_syntax_parser_list_rules_push(parser, cb, NULL, list, + LXB_CSS_SYNTAX_TOKEN_UNDEF); + if (rule == NULL) { + goto failed; + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + parser->status = lxb_css_syntax_parser_run(parser); + if (parser->status != LXB_STATUS_OK) { + goto failed; + } + + parser->stage = LXB_CSS_PARSER_END; + + return list; + +failed: + + if (list != NULL) { + lxb_css_rule_list_destroy(list, true); + } + + parser->stage = LXB_CSS_PARSER_END; + + return NULL; +} + +lxb_css_rule_declaration_list_t * +lxb_css_syntax_parse_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_declarations_t *cb, + const lxb_char_t *data, size_t length) +{ + lxb_css_rule_declaration_list_t *list; + lxb_css_syntax_rule_t *rule; + + if (lxb_css_parser_is_running(parser)) { + parser->status = LXB_STATUS_ERROR_WRONG_STAGE; + return NULL; + } + + lxb_css_parser_clean(parser); + lxb_css_parser_buffer_set(parser, data, length); + + list = lxb_css_rule_declaration_list_create(parser->memory); + if (list == NULL) { + parser->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + goto failed; + } + + rule = lxb_css_syntax_parser_declarations_push(parser, cb, NULL, list, + LXB_CSS_SYNTAX_TOKEN_UNDEF, + true, false); + if (rule == NULL) { + goto failed; + } + + parser->tkz->with_comment = false; + parser->stage = LXB_CSS_PARSER_RUN; + + parser->status = lxb_css_syntax_parser_run(parser); + if (parser->status != LXB_STATUS_OK) { + goto failed; + } + + parser->stage = LXB_CSS_PARSER_END; + + return list; + +failed: + + if (list != NULL) { + lxb_css_rule_declaration_list_destroy(list, true); + } + + parser->stage = LXB_CSS_PARSER_END; + + return NULL; +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *list_rules, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + return lxb_css_syntax_parser_list_rules_push(parser, list_rules, back, + ctx, stop); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_at_rule_t *at_rule, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + if (token->type != LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD) { + return NULL; + } + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0 + && parser->types_pos[-1] == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) + { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_syntax_parser_at_rule_push(parser, at_rule, back, ctx, + stop, false); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_qualified_rule_t *qualified, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_token_type_t type; + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0) { + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + type = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + break; + + default: + type = LXB_CSS_SYNTAX_TOKEN_UNDEF; + break; + } + + + if (parser->types_pos[-1] == type) { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + } + + return lxb_css_syntax_parser_qualified_push(parser, qualified, back, + ctx, stop, false); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_block_t *block, + lxb_css_parser_state_f back, void *ctx) +{ + if (token->type != LXB_CSS_SYNTAX_TOKEN_LC_BRACKET) { + return NULL; + } + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0 + && parser->types_pos[-1] == LXB_CSS_SYNTAX_TOKEN_RC_BRACKET) + { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_syntax_parser_block_push(parser, block, back, ctx); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_declarations_t *declr, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_token_type_t type; + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0) { + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + type = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + break; + + default: + type = LXB_CSS_SYNTAX_TOKEN_UNDEF; + break; + } + + + if (parser->types_pos[-1] == type) { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + } + + return lxb_css_syntax_parser_declarations_push(parser, declr, back, + ctx, stop, true, false); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_components_t *comp, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop) +{ + lxb_css_syntax_token_type_t type; + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0) { + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RS_BRACKET; + break; + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + type = LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS; + break; + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + type = LXB_CSS_SYNTAX_TOKEN_RC_BRACKET; + break; + + default: + type = LXB_CSS_SYNTAX_TOKEN_UNDEF; + break; + } + + + if (parser->types_pos[-1] == type) { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + } + + return lxb_css_syntax_parser_components_push(parser, comp, back, ctx, stop); +} + +lxb_css_syntax_rule_t * +lxb_css_syntax_consume_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_function_t *func, + lxb_css_parser_state_f back, void *ctx) +{ + if (token->type != LXB_CSS_SYNTAX_TOKEN_FUNCTION) { + return NULL; + } + + if (parser->rules > parser->rules_begin && parser->rules->deep != 0 + && parser->types_pos[-1] == LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS) + { + parser->types_pos -= 1; + parser->rules->deep -= 1; + } + + lxb_css_syntax_parser_consume(parser); + + return lxb_css_syntax_parser_function_push(parser, func, back, ctx); +} + +lxb_status_t +lxb_css_syntax_stack_expand(lxb_css_parser_t *parser, size_t count) +{ + size_t length, cur_len, size; + lxb_css_syntax_rule_t *p; + + cur_len = parser->rules - parser->rules_begin; + + length = cur_len + count + 1024; + size = length * sizeof(lxb_css_syntax_rule_t); + + p = lexbor_realloc(parser->rules_begin, size); + if (p == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + parser->rules_begin = p; + parser->rules_end = p + length; + parser->rules = p + cur_len; + + return LXB_STATUS_OK; +} + +void +lxb_css_syntax_codepoint_to_ascii(lxb_css_syntax_tokenizer_t *tkz, + lxb_codepoint_t cp) +{ + /* + * Zero, or is for a surrogate, or is greater than + * the maximum allowed code point (tkz->num > 0x10FFFF). + */ + if (cp == 0 || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) { + memcpy(tkz->pos, lexbor_str_res_ansi_replacement_character, 3); + + tkz->pos += 3; + *tkz->pos = '\0'; + + return; + } + + lxb_char_t *data = tkz->pos; + + if (cp <= 0x0000007F) { + /* 0xxxxxxx */ + data[0] = (lxb_char_t) cp; + + tkz->pos += 1; + } + else if (cp <= 0x000007FF) { + /* 110xxxxx 10xxxxxx */ + data[0] = (char)(0xC0 | (cp >> 6 )); + data[1] = (char)(0x80 | (cp & 0x3F)); + + tkz->pos += 2; + } + else if (cp <= 0x0000FFFF) { + /* 1110xxxx 10xxxxxx 10xxxxxx */ + data[0] = (char)(0xE0 | ((cp >> 12))); + data[1] = (char)(0x80 | ((cp >> 6 ) & 0x3F)); + data[2] = (char)(0x80 | ( cp & 0x3F)); + + tkz->pos += 3; + } + else if (cp <= 0x001FFFFF) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + data[0] = (char)(0xF0 | ( cp >> 18)); + data[1] = (char)(0x80 | ((cp >> 12) & 0x3F)); + data[2] = (char)(0x80 | ((cp >> 6 ) & 0x3F)); + data[3] = (char)(0x80 | ( cp & 0x3F)); + + tkz->pos += 4; + } + + *tkz->pos = '\0'; +} + +lxb_status_t +lxb_css_syntax_ident_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t ch; + lxb_status_t status; + const char **hex_map; + const lxb_char_t *p = data, *end; + + static const lexbor_str_t str_s = lexbor_str("\\"); + + end = data + length; + hex_map = lexbor_str_res_char_to_two_hex_value_lowercase; + + while (p < end) { + ch = *p; + + if (lxb_css_syntax_res_name_map[ch] == 0x00) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_s.data, str_s.length, ctx, status); + lexbor_serialize_write(cb, hex_map[ch], 2, ctx, status); + + data = ++p; + + if (p < end && lexbor_str_res_map_hex[*p] != 0xff) { + lexbor_serialize_write(cb, lxb_css_syntax_str_ws.data, + lxb_css_syntax_str_ws.length, + ctx, status); + } + + continue; + } + + p++; + } + + if (data < p) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_char_t ch; + lxb_status_t status; + const char **hex_map; + const lxb_char_t *p, *end; + + static const lexbor_str_t str_s = lexbor_str("\\"); + static const lexbor_str_t str_dk = lexbor_str("\""); + static const lexbor_str_t str_ds = lexbor_str("\\\\"); + static const lexbor_str_t str_dks = lexbor_str("\\\""); + + p = data; + end = data + length; + hex_map = lexbor_str_res_char_to_two_hex_value_lowercase; + + lexbor_serialize_write(cb, str_dk.data, str_dk.length, ctx, status); + + while (p < end) { + ch = *p; + + if (lxb_css_syntax_res_name_map[ch] == 0x00) { + switch (ch) { + case '\\': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_ds.data, str_ds.length, + ctx, status); + break; + + case '"': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_dks.data, str_dks.length, + ctx, status); + break; + + case '\n': + case '\t': + case '\r': + lexbor_serialize_write(cb, data, p - data, ctx, status); + lexbor_serialize_write(cb, str_s.data, str_s.length, + ctx, status); + lexbor_serialize_write(cb, hex_map[ch], 2, ctx, status); + + p++; + + if (p < end && lexbor_str_res_map_hex[*p] != 0xff) { + lexbor_serialize_write(cb, lxb_css_syntax_str_ws.data, + lxb_css_syntax_str_ws.length, + ctx, status); + } + + data = p; + continue; + + default: + p++; + continue; + } + + data = ++p; + continue; + } + + p++; + } + + if (data < p) { + lexbor_serialize_write(cb, data, p - data, ctx, status); + } + + lexbor_serialize_write(cb, str_dk.data, str_dk.length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_ident_or_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_char_t *p, *end; + + p = data; + end = data + length; + + while (p < end) { + if (lxb_css_syntax_res_name_map[*p++] == 0x00) { + return lxb_css_syntax_string_serialize(data, length, cb, ctx); + } + } + + return cb(data, length, ctx); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.h new file mode 100644 index 0000000..08e9c5c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/syntax.h @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2022-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_H +#define LEXBOR_CSS_SYNTAX_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/syntax/tokenizer.h" +#include "lexbor/css/syntax/tokenizer.h" + + +typedef struct lxb_css_syntax_rule lxb_css_syntax_rule_t; + +typedef const lxb_css_syntax_token_t * +(*lxb_css_syntax_state_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_rule_t *rule); + + +typedef struct lxb_css_syntax_cb_base lxb_css_syntax_cb_base_t; +typedef struct lxb_css_syntax_cb_list_rules lxb_css_syntax_cb_list_rules_t; +typedef struct lxb_css_syntax_cb_at_rule lxb_css_syntax_cb_at_rule_t; +typedef struct lxb_css_syntax_cb_qualified_rule lxb_css_syntax_cb_qualified_rule_t; +typedef struct lxb_css_syntax_cb_block lxb_css_syntax_cb_block_t; +typedef struct lxb_css_syntax_cb_declarations lxb_css_syntax_cb_declarations_t; +typedef struct lxb_css_syntax_cb_function lxb_css_syntax_cb_function_t; +typedef struct lxb_css_syntax_cb_components lxb_css_syntax_cb_components_t; +typedef struct lxb_css_syntax_cb_pipe lxb_css_syntax_cb_pipe_t; + +typedef struct lxb_css_syntax_declaration_offset lxb_css_syntax_declaration_offset_t; + +typedef lxb_status_t +(*lxb_css_syntax_cb_done_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, bool failed); + +typedef const lxb_css_syntax_cb_at_rule_t * +(*lxb_css_syntax_begin_at_rule_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +typedef const lxb_css_syntax_cb_qualified_rule_t * +(*lxb_css_syntax_begin_qualified_rule_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +typedef const lxb_css_syntax_cb_block_t * +(*lxb_css_syntax_begin_block_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); + +typedef const lxb_css_syntax_cb_declarations_t * +(*lxb_css_syntax_begin_declarations_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +typedef lxb_css_parser_state_f +(*lxb_css_syntax_declaration_name_f)(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + void *ctx, void **out_rule); +typedef lxb_status_t +(*lxb_css_syntax_declaration_end_f)(lxb_css_parser_t *parser, + void *declaration, void *ctx, + const lxb_css_syntax_token_t *token, + lxb_css_syntax_declaration_offset_t *offset, + bool important, bool failed); + +struct lxb_css_syntax_cb_base { + lxb_css_parser_state_f failed; + lxb_css_syntax_cb_done_f end; +}; + +struct lxb_css_syntax_cb_list_rules { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f next; + lxb_css_syntax_begin_at_rule_f at_rule; + lxb_css_syntax_begin_qualified_rule_f qualified_rule; +}; + +struct lxb_css_syntax_cb_at_rule { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f prelude; + lxb_css_syntax_cb_done_f prelude_end; + lxb_css_syntax_begin_block_f block; +}; + +struct lxb_css_syntax_cb_qualified_rule { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f prelude; + lxb_css_syntax_cb_done_f prelude_end; + lxb_css_syntax_begin_block_f block; +}; + +struct lxb_css_syntax_cb_block { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f next; + lxb_css_syntax_begin_at_rule_f at_rule; + lxb_css_syntax_begin_declarations_f declarations; + lxb_css_syntax_begin_qualified_rule_f qualified_rule; +}; + +struct lxb_css_syntax_cb_declarations { + lxb_css_syntax_cb_base_t cb; + lxb_css_syntax_declaration_name_f name; + lxb_css_syntax_declaration_end_f end; +}; + +struct lxb_css_syntax_cb_function { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f value; +}; + +struct lxb_css_syntax_cb_components { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f prelude; +}; + +struct lxb_css_syntax_cb_pipe { + lxb_css_syntax_cb_base_t cb; + lxb_css_parser_state_f prelude; +}; + +struct lxb_css_syntax_declaration_offset { + size_t value_begin; + size_t value_end; + size_t important_begin; + size_t important_end; + size_t end; +}; + +struct lxb_css_syntax_rule { + lxb_css_syntax_state_f phase; + lxb_css_parser_state_f state; + + /* + * This callback will be called before rule->state is called. + * Exclusively from the lxb_css_parser_run(...). + */ + lxb_css_syntax_state_f back; + lxb_css_parser_state_f back_state; + void *context; + void *context_old; + void *returned; + + union { + const lxb_css_syntax_cb_base_t *cb; + const lxb_css_syntax_cb_list_rules_t *list_rules; + const lxb_css_syntax_cb_at_rule_t *at_rule; + const lxb_css_syntax_cb_qualified_rule_t *qualified_rule; + const lxb_css_syntax_cb_components_t *components; + const lxb_css_syntax_cb_declarations_t *declarations; + const lxb_css_syntax_cb_function_t *func; + const lxb_css_syntax_cb_block_t *block; + const lxb_css_syntax_cb_pipe_t *pipe; + void *user; + } cbx; + + size_t offset; + size_t deep; + size_t begin; + lxb_css_syntax_token_type_t block_end; + bool nested; + bool skip_consume; + bool important; + bool failed; +}; + + +LXB_API lxb_css_rule_list_t * +lxb_css_syntax_parse_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *cb, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_rule_declaration_list_t * +lxb_css_syntax_parse_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_declarations_t *cb, + const lxb_char_t *data, size_t length); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_list_rules(lxb_css_parser_t *parser, + const lxb_css_syntax_cb_list_rules_t *list_rules, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_at_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_at_rule_t *at_rule, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_qualified_rule(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_qualified_rule_t *qualified, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_block(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_block_t *block, + lxb_css_parser_state_f back, void *ctx); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_declarations(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_declarations_t *declr, + lxb_css_parser_state_f back, void *ctx, + lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_components(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_components_t *comp, + lxb_css_parser_state_f back, + void *ctx, lxb_css_syntax_token_type_t stop); + +LXB_API lxb_css_syntax_rule_t * +lxb_css_syntax_consume_function(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const lxb_css_syntax_cb_function_t *func, + lxb_css_parser_state_f back, void *ctx); + + +LXB_API lxb_status_t +lxb_css_syntax_stack_expand(lxb_css_parser_t *parser, size_t count); + +LXB_API void +lxb_css_syntax_codepoint_to_ascii(lxb_css_syntax_tokenizer_t *tkz, + lxb_codepoint_t cp); + +LXB_API lxb_status_t +lxb_css_syntax_ident_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_syntax_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_syntax_ident_or_string_serialize(const lxb_char_t *data, size_t length, + lexbor_serialize_cb_f cb, void *ctx); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.c new file mode 100644 index 0000000..fdfc621 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.c @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/shs.h" +#include "lexbor/core/conv.h" +#include "lexbor/core/serialize.h" +#include "lexbor/core/print.h" + +#include "lexbor/css/parser.h" +#include "lexbor/css/syntax/token.h" +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/token_res.h" + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_ansi_replacement_character[4]; + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex[256]; +#endif + +lxb_css_syntax_token_t * +lxb_css_syntax_tokenizer_token(lxb_css_syntax_tokenizer_t *tkz); + + +typedef struct { + lexbor_str_t *str; + lexbor_mraw_t *mraw; +} +lxb_css_syntax_token_ctx_t; + + +static lxb_status_t +lxb_css_syntax_token_str_cb(const lxb_char_t *data, size_t len, void *ctx); + +static int8_t +lxb_css_syntax_token_encode_utf_8(lxb_char_t *data, lxb_codepoint_t cp); + + +lxb_css_syntax_token_t * +lxb_css_syntax_token(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz->first != NULL) { + return tkz->first; + } + + return lxb_css_syntax_tokenizer_token(tkz); +} + +lxb_css_syntax_token_t * +lxb_css_syntax_token_next(lxb_css_syntax_tokenizer_t *tkz) +{ + return lxb_css_syntax_tokenizer_token(tkz); +} + +void +lxb_css_syntax_token_consume(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_css_syntax_token_t *token; + + if (tkz->first) { + token = tkz->first; + tkz->first = token->next; + + if (tkz->last == token) { + tkz->last = NULL; + } + + lxb_css_syntax_token_string_free(tkz, token); + lexbor_dobject_free(tkz->tokens, token); + } +} + +void +lxb_css_syntax_token_consume_n(lxb_css_syntax_tokenizer_t *tkz, unsigned count) +{ + while (count != 0) { + count--; + lxb_css_syntax_token_consume(tkz); + } +} + +lxb_status_t +lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw) +{ + size_t length; + + length = token->length + 1; + + if (length > str->length) { + if (str->data == NULL) { + str->data = lexbor_mraw_alloc(mraw, length); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + str->length = 0; + } + else { + if (lexbor_str_realloc(str, mraw, length) == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + } + + /* + 1 = '\0' */ + memcpy(str->data, token->data, length); + + str->length = token->length; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_token_string_make(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token) +{ + lxb_char_t *data; + lxb_css_syntax_token_string_t *token_string; + + if (token->type >= LXB_CSS_SYNTAX_TOKEN_IDENT + && token->type <= LXB_CSS_SYNTAX_TOKEN_WHITESPACE) + { + token_string = lxb_css_syntax_token_string(token); + goto copy; + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_DIMENSION) { + token_string = lxb_css_syntax_token_dimension_string(token); + goto copy; + } + + return LXB_STATUS_OK; + +copy: + + data = lexbor_mraw_alloc(tkz->mraw, token_string->length + 1); + if (data == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return tkz->status; + } + + /* + 1 = '\0' */ + memcpy(data, token_string->data, token_string->length + 1); + + token_string->data = data; + token->cloned = true; + + return LXB_STATUS_OK; +} + +void +lxb_css_syntax_token_string_free(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token) +{ + lxb_css_syntax_token_string_t *token_string; + + if (token->cloned) { + if (token->type == LXB_CSS_SYNTAX_TOKEN_DIMENSION) { + token_string = lxb_css_syntax_token_dimension_string(token); + } + else { + token_string = lxb_css_syntax_token_string(token); + } + + lexbor_mraw_free(tkz->mraw, (lxb_char_t *) token_string->data); + } +} + +const lxb_char_t * +lxb_css_syntax_token_type_name_by_id(lxb_css_syntax_token_type_t type) +{ + switch (type) { + case LXB_CSS_SYNTAX_TOKEN_IDENT: + return (lxb_char_t *) "ident"; + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + return (lxb_char_t *) "function"; + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + return (lxb_char_t *) "at-keyword"; + case LXB_CSS_SYNTAX_TOKEN_HASH: + return (lxb_char_t *) "hash"; + case LXB_CSS_SYNTAX_TOKEN_STRING: + return (lxb_char_t *) "string"; + case LXB_CSS_SYNTAX_TOKEN_BAD_STRING: + return (lxb_char_t *) "bad-string"; + case LXB_CSS_SYNTAX_TOKEN_URL: + return (lxb_char_t *) "url"; + case LXB_CSS_SYNTAX_TOKEN_BAD_URL: + return (lxb_char_t *) "bad-url"; + case LXB_CSS_SYNTAX_TOKEN_DELIM: + return (lxb_char_t *) "delim"; + case LXB_CSS_SYNTAX_TOKEN_UNICODE_RANGE: + return (lxb_char_t *) "unicode-range"; + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + return (lxb_char_t *) "number"; + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + return (lxb_char_t *) "percentage"; + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + return (lxb_char_t *) "dimension"; + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + return (lxb_char_t *) "whitespace"; + case LXB_CSS_SYNTAX_TOKEN_CDO: + return (lxb_char_t *) "CDO"; + case LXB_CSS_SYNTAX_TOKEN_CDC: + return (lxb_char_t *) "CDC"; + case LXB_CSS_SYNTAX_TOKEN_COLON: + return (lxb_char_t *) "colon"; + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return (lxb_char_t *) "semicolon"; + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return (lxb_char_t *) "comma"; + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + return (lxb_char_t *) "left-square-bracket"; + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + return (lxb_char_t *) "right-square-bracket"; + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + return (lxb_char_t *) "left-parenthesis"; + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + return (lxb_char_t *) "right-parenthesis"; + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + return (lxb_char_t *) "left-curly-bracket"; + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + return (lxb_char_t *) "right-curly-bracket"; + case LXB_CSS_SYNTAX_TOKEN_COMMENT: + return (lxb_char_t *) "comment"; + case LXB_CSS_SYNTAX_TOKEN__EOF: + return (lxb_char_t *) "end-of-file"; + case LXB_CSS_SYNTAX_TOKEN__END: + return (lxb_char_t *) "end"; + default: + return (lxb_char_t *) "undefined"; + } +} + +lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_id_by_name(const lxb_char_t *type_name, size_t len) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_syntax_token_res_name_shs_map, + type_name, len); + if (entry == NULL) { + return LXB_CSS_SYNTAX_TOKEN_UNDEF; + } + + return (lxb_css_syntax_token_type_t) (uintptr_t) entry->value; +} + + +lxb_status_t +lxb_css_syntax_token_serialize(const lxb_css_syntax_token_t *token, + lxb_css_syntax_token_cb_f cb, void *ctx) +{ + size_t len; + lxb_status_t status; + lxb_char_t buf[128]; + const lxb_css_syntax_token_string_t *str; + const lxb_css_syntax_token_dimension_t *dim; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + len = lxb_css_syntax_token_encode_utf_8(buf, + token->types.delim.character); + buf[len] = 0x00; + + return cb(buf, len, ctx); + + case LXB_CSS_SYNTAX_TOKEN_UNICODE_RANGE: + /* Start */ + buf[0] = 'U'; + buf[1] = '+'; + len = 2; + len += lexbor_conv_dec_to_hex(token->types.unicode_range.start, + &buf[len], (sizeof(buf) - 1) - len, + true); + + /* End */ + buf[len] = '-'; + len += 1; + len += lexbor_conv_dec_to_hex(token->types.unicode_range.end, + &buf[len], (sizeof(buf) - 1) - len, + true); + buf[len] = 0x00; + + return cb(buf, len, ctx); + + case LXB_CSS_SYNTAX_TOKEN_NUMBER: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + return cb(buf, len, ctx); + + case LXB_CSS_SYNTAX_TOKEN_PERCENTAGE: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + status = cb(buf, len, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "%", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_CDO: + return cb((lxb_char_t *) "", 3, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COLON: + return cb((lxb_char_t *) ":", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return cb((lxb_char_t *) ";", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COMMA: + return cb((lxb_char_t *) ",", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_LS_BRACKET: + return cb((lxb_char_t *) "[", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_RS_BRACKET: + return cb((lxb_char_t *) "]", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS: + return cb((lxb_char_t *) "(", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS: + return cb((lxb_char_t *) ")", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_LC_BRACKET: + return cb((lxb_char_t *) "{", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_RC_BRACKET: + return cb((lxb_char_t *) "}", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_HASH: + status = cb((lxb_char_t *) "#", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD: + status = cb((lxb_char_t *) "@", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_WHITESPACE: + case LXB_CSS_SYNTAX_TOKEN_IDENT: + str = &token->types.string; + + return cb(str->data, str->length, ctx); + + case LXB_CSS_SYNTAX_TOKEN_FUNCTION: + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "(", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_STRING: + case LXB_CSS_SYNTAX_TOKEN_BAD_STRING: { + status = cb((lxb_char_t *) "\"", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + const lxb_char_t *begin = token->types.string.data; + const lxb_char_t *end = begin + token->types.string.length; + + const lxb_char_t *ptr = begin; + + for (; begin < end; begin++) { + /* 0x5C; '\'; Inverse/backward slash */ + if (*begin == 0x5C) { + begin += 1; + + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (begin == end) { + status = cb((const lxb_char_t *) "\\", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ptr = begin; + + break; + } + + begin -= 1; + ptr = begin; + } + /* 0x22; '"'; Only quotes above */ + else if (*begin == 0x22) { + if (ptr != begin) { + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + status = cb((const lxb_char_t *) "\\", 1, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + ptr = begin; + } + } + + if (ptr != begin) { + status = cb(ptr, (begin - ptr), ctx); + if (status != LXB_STATUS_OK) { + return status; + } + } + + return cb((const lxb_char_t *) "\"", 1, ctx); + } + + case LXB_CSS_SYNTAX_TOKEN_URL: + case LXB_CSS_SYNTAX_TOKEN_BAD_URL: + status = cb((lxb_char_t *) "url(", 4, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) ")", 1, ctx); + + case LXB_CSS_SYNTAX_TOKEN_COMMENT: + status = cb((lxb_char_t *) "/*", 2, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + str = &token->types.string; + + status = cb(str->data, str->length, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb((lxb_char_t *) "*/", 2, ctx); + + case LXB_CSS_SYNTAX_TOKEN_DIMENSION: + len = lexbor_conv_float_to_data(token->types.number.num, + buf, (sizeof(buf) - 1)); + + buf[len] = 0x00; + + status = cb(buf, len, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + dim = &token->types.dimension; + + return cb(dim->str.data, dim->str.length, ctx); + + case LXB_CSS_SYNTAX_TOKEN__EOF: + return cb((lxb_char_t *) "END-OF-FILE", 11, ctx); + + case LXB_CSS_SYNTAX_TOKEN__END: + return cb((lxb_char_t *) "END", 3, ctx); + + default: + return LXB_STATUS_ERROR; + } +} + +lxb_status_t +lxb_css_syntax_token_serialize_str(const lxb_css_syntax_token_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw) +{ + lxb_css_syntax_token_ctx_t ctx; + + ctx.str = str; + ctx.mraw = mraw; + + if (str->data == NULL) { + lexbor_str_init(str, mraw, 1); + if (str->data == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + } + + return lxb_css_syntax_token_serialize(token, lxb_css_syntax_token_str_cb, + &ctx); +} + +static lxb_status_t +lxb_css_syntax_token_str_cb(const lxb_char_t *data, size_t len, void *cb_ctx) +{ + lxb_char_t *ptr; + lxb_css_syntax_token_ctx_t *ctx = (lxb_css_syntax_token_ctx_t *) cb_ctx; + + ptr = lexbor_str_append(ctx->str, ctx->mraw, data, len); + if (ptr == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + return LXB_STATUS_OK; +} + + +lxb_char_t * +lxb_css_syntax_token_serialize_char(const lxb_css_syntax_token_t *token, + size_t *out_length) +{ + size_t length = 0; + lxb_status_t status; + lexbor_str_t str; + + status = lxb_css_syntax_token_serialize(token, lexbor_serialize_length_cb, + &length); + if (status != LXB_STATUS_OK) { + goto failed; + } + + /* + 1 == '\0' */ + str.data = lexbor_malloc(length + 1); + if (str.data == NULL) { + goto failed; + } + + str.length = 0; + + status = lxb_css_syntax_token_serialize(token, lexbor_serialize_copy_cb, + &str); + if (status != LXB_STATUS_OK) { + lexbor_free(str.data); + goto failed; + } + + str.data[str.length] = '\0'; + + if (out_length != NULL) { + *out_length = str.length; + } + + return str.data; + +failed: + + if (out_length != NULL) { + *out_length = 0; + } + + return NULL; +} + +lxb_css_log_message_t * +lxb_css_syntax_token_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const char *module_name) +{ + (void) token; (void) module_name; + + /* Size patch: the token text is never read by the shim (parser->status is + * the only observable), so it is not serialized; callers only require a + * non-NULL message record. */ + return lxb_css_log_push(parser->log, LXB_CSS_LOG_SYNTAX_ERROR, 0); +} + +static int8_t +lxb_css_syntax_token_encode_utf_8(lxb_char_t *data, lxb_codepoint_t cp) +{ + if (cp < 0x80) { + /* 0xxxxxxx */ + *data = (lxb_char_t) cp; + + return 1; + } + + if (cp < 0x800) { + /* 110xxxxx 10xxxxxx */ + *data++ = (lxb_char_t) (0xC0 | (cp >> 6 )); + *data = (lxb_char_t) (0x80 | (cp & 0x3F)); + + return 2; + } + + if (cp < 0x10000) { + /* 1110xxxx 10xxxxxx 10xxxxxx */ + *data++ = (lxb_char_t) (0xE0 | ((cp >> 12))); + *data++ = (lxb_char_t) (0x80 | ((cp >> 6 ) & 0x3F)); + *data = (lxb_char_t) (0x80 | ( cp & 0x3F)); + + return 3; + } + + if (cp < 0x110000) { + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + *data++ = (lxb_char_t) (0xF0 | ( cp >> 18)); + *data++ = (lxb_char_t) (0x80 | ((cp >> 12) & 0x3F)); + *data++ = (lxb_char_t) (0x80 | ((cp >> 6 ) & 0x3F)); + *data = (lxb_char_t) (0x80 | ( cp & 0x3F)); + + return 4; + } + + return 0; +} + +/* + * No inline functions for ABI. + */ +lxb_css_syntax_token_t * +lxb_css_syntax_token_create_noi(lexbor_dobject_t *dobj) +{ + return lxb_css_syntax_token_create(dobj); +} + +void +lxb_css_syntax_token_clean_noi(lxb_css_syntax_token_t *token) +{ + lxb_css_syntax_token_clean(token); +} + +lxb_css_syntax_token_t * +lxb_css_syntax_token_destroy_noi(lxb_css_syntax_token_t *token, + lexbor_dobject_t *dobj) +{ + return lxb_css_syntax_token_destroy(token, dobj); +} + +const lxb_char_t * +lxb_css_syntax_token_type_name_noi(lxb_css_syntax_token_t *token) +{ + return lxb_css_syntax_token_type_name(token); +} + +lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_noi(lxb_css_syntax_token_t *token) +{ + return lxb_css_syntax_token_type(token); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.h new file mode 100644 index 0000000..229ebb1 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token.h @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_TOKEN_H +#define LEXBOR_CSS_SYNTAX_TOKEN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/str.h" + +#include "lexbor/css/log.h" +#include "lexbor/css/syntax/base.h" + + +#define lxb_css_syntax_token_base(token) ((lxb_css_syntax_token_base_t *) (token)) +#define lxb_css_syntax_token_ident(token) ((lxb_css_syntax_token_ident_t *) (token)) +#define lxb_css_syntax_token_function(token) ((lxb_css_syntax_token_function_t *) (token)) +#define lxb_css_syntax_token_at_keyword(token) ((lxb_css_syntax_token_at_keyword_t *) (token)) +#define lxb_css_syntax_token_hash(token) ((lxb_css_syntax_token_hash_t *) (token)) +#define lxb_css_syntax_token_string(token) ((lxb_css_syntax_token_string_t *) (token)) +#define lxb_css_syntax_token_bad_string(token) ((lxb_css_syntax_token_bad_string_t *) (token)) +#define lxb_css_syntax_token_url(token) ((lxb_css_syntax_token_url_t *) (token)) +#define lxb_css_syntax_token_bad_url(token) ((lxb_css_syntax_token_bad_url_t *) (token)) +#define lxb_css_syntax_token_delim(token) ((lxb_css_syntax_token_delim_t *) (token)) +#define lxb_css_syntax_token_delim_char(token) (((lxb_css_syntax_token_delim_t *) (token))->character) +#define lxb_css_syntax_token_unicode_range(token) ((lxb_css_syntax_token_unicode_range_t *) (token)) +#define lxb_css_syntax_token_number(token) ((lxb_css_syntax_token_number_t *) (token)) +#define lxb_css_syntax_token_percentage(token) ((lxb_css_syntax_token_percentage_t *) (token)) +#define lxb_css_syntax_token_dimension(token) ((lxb_css_syntax_token_dimension_t *) (token)) +#define lxb_css_syntax_token_dimension_string(token) (&((lxb_css_syntax_token_dimension_t *) (token))->str) +#define lxb_css_syntax_token_whitespace(token) ((lxb_css_syntax_token_whitespace_t *) (token)) +#define lxb_css_syntax_token_cdo(token) ((lxb_css_syntax_token_cdo_t *) (token)) +#define lxb_css_syntax_token_cdc(token) ((lxb_css_syntax_token_cdc_t *) (token)) +#define lxb_css_syntax_token_colon(token) ((lxb_css_syntax_token_colon_t *) (token)) +#define lxb_css_syntax_token_semicolon(token) ((lxb_css_syntax_token_semicolon_t *) (token)) +#define lxb_css_syntax_token_comma(token) ((lxb_css_syntax_token_comma_t *) (token)) +#define lxb_css_syntax_token_ls_bracket(token) ((lxb_css_syntax_token_ls_bracket_t *) (token)) +#define lxb_css_syntax_token_rs_bracket(token) ((lxb_css_syntax_token_rs_bracket_t *) (token)) +#define lxb_css_syntax_token_l_parenthesis(token) ((lxb_css_syntax_token_l_parenthesis_t *) (token)) +#define lxb_css_syntax_token_r_parenthesis(token) ((lxb_css_syntax_token_r_parenthesis_t *) (token)) +#define lxb_css_syntax_token_lc_bracket(token) ((lxb_css_syntax_token_lc_bracket_t *) (token)) +#define lxb_css_syntax_token_rc_bracket(token) ((lxb_css_syntax_token_rc_bracket_t *) (token)) +#define lxb_css_syntax_token_comment(token) ((lxb_css_syntax_token_comment_t *) (token)) + + +typedef struct lxb_css_syntax_token_data lxb_css_syntax_token_data_t; + +typedef const lxb_char_t * +(*lxb_css_syntax_token_data_cb_f)(const lxb_char_t *begin, const lxb_char_t *end, + lexbor_str_t *str, lexbor_mraw_t *mraw, + lxb_css_syntax_token_data_t *td); + +typedef lxb_status_t +(*lxb_css_syntax_token_cb_f)(const lxb_char_t *data, size_t len, void *ctx); + +struct lxb_css_syntax_token_data { + lxb_css_syntax_token_data_cb_f cb; + lxb_status_t status; + int count; + uint32_t num; + bool is_last; +}; + +typedef enum { + LXB_CSS_SYNTAX_TOKEN_UNDEF = 0x00, + + /* String tokens. */ + LXB_CSS_SYNTAX_TOKEN_IDENT, + LXB_CSS_SYNTAX_TOKEN_FUNCTION, + LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD, + LXB_CSS_SYNTAX_TOKEN_HASH, + LXB_CSS_SYNTAX_TOKEN_STRING, + LXB_CSS_SYNTAX_TOKEN_BAD_STRING, + LXB_CSS_SYNTAX_TOKEN_URL, + LXB_CSS_SYNTAX_TOKEN_BAD_URL, + LXB_CSS_SYNTAX_TOKEN_COMMENT, /* not in specification */ + LXB_CSS_SYNTAX_TOKEN_WHITESPACE, + + /* Has a string. */ + LXB_CSS_SYNTAX_TOKEN_DIMENSION, + + /* Other tokens. */ + LXB_CSS_SYNTAX_TOKEN_DELIM, + LXB_CSS_SYNTAX_TOKEN_UNICODE_RANGE, + LXB_CSS_SYNTAX_TOKEN_NUMBER, + LXB_CSS_SYNTAX_TOKEN_PERCENTAGE, + LXB_CSS_SYNTAX_TOKEN_CDO, + LXB_CSS_SYNTAX_TOKEN_CDC, + LXB_CSS_SYNTAX_TOKEN_COLON, + LXB_CSS_SYNTAX_TOKEN_SEMICOLON, + LXB_CSS_SYNTAX_TOKEN_COMMA, + LXB_CSS_SYNTAX_TOKEN_LS_BRACKET, /* U+005B LEFT SQUARE BRACKET ([) */ + LXB_CSS_SYNTAX_TOKEN_RS_BRACKET, /* U+005D RIGHT SQUARE BRACKET (]) */ + LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS, /* U+0028 LEFT PARENTHESIS (() */ + LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS, /* U+0029 RIGHT PARENTHESIS ()) */ + LXB_CSS_SYNTAX_TOKEN_LC_BRACKET, /* U+007B LEFT CURLY BRACKET ({) */ + LXB_CSS_SYNTAX_TOKEN_RC_BRACKET, /* U+007D RIGHT CURLY BRACKET (}) */ + LXB_CSS_SYNTAX_TOKEN__EOF, + LXB_CSS_SYNTAX_TOKEN__TERMINATED, /* Deprecated, use LXB_CSS_SYNTAX_TOKEN__END. */ + LXB_CSS_SYNTAX_TOKEN__END = LXB_CSS_SYNTAX_TOKEN__TERMINATED, + LXB_CSS_SYNTAX_TOKEN__LAST_ENTRY +} +lxb_css_syntax_token_type_t; + +typedef struct lxb_css_syntax_token_base { + const lxb_char_t *begin; + size_t length; + + uintptr_t user_id; +} +lxb_css_syntax_token_base_t; + +typedef struct lxb_css_syntax_token_number { + lxb_css_syntax_token_base_t base; + + double num; + bool is_float; + bool have_sign; +} +lxb_css_syntax_token_number_t; + +typedef struct lxb_css_syntax_token_string { + lxb_css_syntax_token_base_t base; + + const lxb_char_t *data; + size_t length; +} +lxb_css_syntax_token_string_t; + +typedef struct lxb_css_syntax_token_dimension { + lxb_css_syntax_token_number_t num; + lxb_css_syntax_token_string_t str; +} +lxb_css_syntax_token_dimension_t; + +typedef struct lxb_css_syntax_token_delim { + lxb_css_syntax_token_base_t base; + lxb_codepoint_t character; +} +lxb_css_syntax_token_delim_t; + +typedef struct lxb_css_syntax_token_unicode_range { + lxb_css_syntax_token_base_t base; + lxb_codepoint_t start; + lxb_codepoint_t end; +} +lxb_css_syntax_token_unicode_range_t; + +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_ident_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_function_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_at_keyword_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_hash_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_bad_string_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_url_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_bad_url_t; +typedef lxb_css_syntax_token_number_t lxb_css_syntax_token_percentage_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_whitespace_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_cdo_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_cdc_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_colon_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_semicolon_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_comma_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_ls_bracket_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_rs_bracket_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_l_parenthesis_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_r_parenthesis_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_lc_bracket_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_rc_bracket_t; +typedef lxb_css_syntax_token_string_t lxb_css_syntax_token_comment_t; +typedef lxb_css_syntax_token_base_t lxb_css_syntax_token_terminated_t; + +struct lxb_css_syntax_token { + union lxb_css_syntax_token_u { + lxb_css_syntax_token_base_t base; + lxb_css_syntax_token_comment_t comment; + lxb_css_syntax_token_number_t number; + lxb_css_syntax_token_dimension_t dimension; + lxb_css_syntax_token_percentage_t percentage; + lxb_css_syntax_token_hash_t hash; + lxb_css_syntax_token_string_t string; + lxb_css_syntax_token_bad_string_t bad_string; + lxb_css_syntax_token_delim_t delim; + lxb_css_syntax_token_unicode_range_t unicode_range; + lxb_css_syntax_token_l_parenthesis_t lparenthesis; + lxb_css_syntax_token_r_parenthesis_t rparenthesis; + lxb_css_syntax_token_cdc_t cdc; + lxb_css_syntax_token_function_t function; + lxb_css_syntax_token_ident_t ident; + lxb_css_syntax_token_url_t url; + lxb_css_syntax_token_bad_url_t bad_url; + lxb_css_syntax_token_at_keyword_t at_keyword; + lxb_css_syntax_token_whitespace_t whitespace; + lxb_css_syntax_token_terminated_t terminated; + } + types; + + lxb_css_syntax_token_type_t type; + uintptr_t offset; + bool cloned; + + lxb_css_syntax_token_t *next; +}; + + +LXB_API lxb_css_syntax_token_t * +lxb_css_syntax_token(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API lxb_css_syntax_token_t * +lxb_css_syntax_token_next(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API void +lxb_css_syntax_token_consume(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API void +lxb_css_syntax_token_consume_n(lxb_css_syntax_tokenizer_t *tkz, unsigned count); + +LXB_API lxb_status_t +lxb_css_syntax_token_string_dup(lxb_css_syntax_token_string_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw); + +LXB_API lxb_status_t +lxb_css_syntax_token_string_make(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token); + +LXB_API void +lxb_css_syntax_token_string_free(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token); + +LXB_API const lxb_char_t * +lxb_css_syntax_token_type_name_by_id(lxb_css_syntax_token_type_t type); + +LXB_API lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_id_by_name(const lxb_char_t *type_name, size_t len); + +LXB_API lxb_status_t +lxb_css_syntax_token_serialize(const lxb_css_syntax_token_t *token, + lxb_css_syntax_token_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_syntax_token_serialize_str(const lxb_css_syntax_token_t *token, + lexbor_str_t *str, lexbor_mraw_t *mraw); + +LXB_API lxb_char_t * +lxb_css_syntax_token_serialize_char(const lxb_css_syntax_token_t *token, + size_t *out_length); + +LXB_API lxb_css_log_message_t * +lxb_css_syntax_token_error(lxb_css_parser_t *parser, + const lxb_css_syntax_token_t *token, + const char *module_name); + +/* + * Inline functions + */ +lxb_inline lxb_css_syntax_token_t * +lxb_css_syntax_token_create(lexbor_dobject_t *dobj) +{ + return (lxb_css_syntax_token_t *) lexbor_dobject_calloc(dobj); +} + +lxb_inline void +lxb_css_syntax_token_clean(lxb_css_syntax_token_t *token) +{ + memset(token, 0, sizeof(lxb_css_syntax_token_t)); +} + +lxb_inline lxb_css_syntax_token_t * +lxb_css_syntax_token_destroy(lxb_css_syntax_token_t *token, + lexbor_dobject_t *dobj) +{ + return (lxb_css_syntax_token_t *) lexbor_dobject_free(dobj, token); +} + +lxb_inline const lxb_char_t * +lxb_css_syntax_token_type_name(const lxb_css_syntax_token_t *token) +{ + return lxb_css_syntax_token_type_name_by_id(token->type); +} + +lxb_inline lxb_css_syntax_token_type_t +lxb_css_syntax_token_type(const lxb_css_syntax_token_t *token) +{ + return token->type; +} + +lxb_inline lxb_css_syntax_token_t * +lxb_css_syntax_token_wo_ws(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_css_syntax_token_t *token; + + token = lxb_css_syntax_token(tkz); + if (token == NULL) { + return NULL; + } + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + lxb_css_syntax_token_consume(tkz); + token = lxb_css_syntax_token(tkz); + } + + return token; +} + +/* + * No inline functions for ABI. + */ +LXB_API lxb_css_syntax_token_t * +lxb_css_syntax_token_create_noi(lexbor_dobject_t *dobj); + +LXB_API void +lxb_css_syntax_token_clean_noi(lxb_css_syntax_token_t *token); + +LXB_API lxb_css_syntax_token_t * +lxb_css_syntax_token_destroy_noi(lxb_css_syntax_token_t *token, + lexbor_dobject_t *dobj); + +LXB_API const lxb_char_t * +lxb_css_syntax_token_type_name_noi(lxb_css_syntax_token_t *token); + +LXB_API lxb_css_syntax_token_type_t +lxb_css_syntax_token_type_noi(lxb_css_syntax_token_t *token); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_TOKEN_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token_res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token_res.h new file mode 100644 index 0000000..d5ee29c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/token_res.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_TOKEN_RES_H +#define LEXBOR_CSS_SYNTAX_TOKEN_RES_H + +static const lexbor_shs_entry_t lxb_css_syntax_token_res_name_shs_map[136] = +{ + {NULL, NULL, 135, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"colon", (void *) LXB_CSS_SYNTAX_TOKEN_COLON, 5, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"undefined", (void *) LXB_CSS_SYNTAX_TOKEN_UNDEF, 9, 0}, {"right-curly-bracket", (void *) LXB_CSS_SYNTAX_TOKEN_RC_BRACKET, 19, 0}, + {"right-square-bracket", (void *) LXB_CSS_SYNTAX_TOKEN_RS_BRACKET, 20, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"delim", (void *) LXB_CSS_SYNTAX_TOKEN_DELIM, 5, 0}, {"left-parenthesis", (void *) LXB_CSS_SYNTAX_TOKEN_L_PARENTHESIS, 16, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"dimension", (void *) LXB_CSS_SYNTAX_TOKEN_DIMENSION, 9, 0}, {"url", (void *) LXB_CSS_SYNTAX_TOKEN_URL, 3, 0}, + {"string", (void *) LXB_CSS_SYNTAX_TOKEN_STRING, 6, 0}, {"comma", (void *) LXB_CSS_SYNTAX_TOKEN_COMMA, 5, 0}, + {NULL, NULL, 0, 0}, {"bad-url", (void *) LXB_CSS_SYNTAX_TOKEN_BAD_URL, 7, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"hash", (void *) LXB_CSS_SYNTAX_TOKEN_HASH, 4, 0}, + {NULL, NULL, 0, 0}, {"ident", (void *) LXB_CSS_SYNTAX_TOKEN_IDENT, 5, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"function", (void *) LXB_CSS_SYNTAX_TOKEN_FUNCTION, 8, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"cdc", (void *) LXB_CSS_SYNTAX_TOKEN_CDC, 3, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"unicode-range", (void *) LXB_CSS_SYNTAX_TOKEN_UNICODE_RANGE, 13, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"left-curly-bracket", (void *) LXB_CSS_SYNTAX_TOKEN_LC_BRACKET, 18, 0}, + {"left-square-bracket", (void *) LXB_CSS_SYNTAX_TOKEN_LS_BRACKET, 19, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"bad-string", (void *) LXB_CSS_SYNTAX_TOKEN_BAD_STRING, 10, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"whitespace", (void *) LXB_CSS_SYNTAX_TOKEN_WHITESPACE, 10, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"cdo", (void *) LXB_CSS_SYNTAX_TOKEN_CDO, 3, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"comment", (void *) LXB_CSS_SYNTAX_TOKEN_COMMENT, 7, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"at-keyword", (void *) LXB_CSS_SYNTAX_TOKEN_AT_KEYWORD, 10, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"right-parenthesis", (void *) LXB_CSS_SYNTAX_TOKEN_R_PARENTHESIS, 17, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"number", (void *) LXB_CSS_SYNTAX_TOKEN_NUMBER, 6, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"percentage", (void *) LXB_CSS_SYNTAX_TOKEN_PERCENTAGE, 10, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {"end-of-file", (void *) LXB_CSS_SYNTAX_TOKEN__EOF, 11, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, {"semicolon", (void *) LXB_CSS_SYNTAX_TOKEN_SEMICOLON, 9, 0} +}; + +#endif /* LEXBOR_CSS_SYNTAX_TOKEN_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.c new file mode 100644 index 0000000..a44b2c7 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.c @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2018-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/tokenizer.h" +#include "lexbor/css/syntax/tokenizer/error.h" +#include "lexbor/css/syntax/state.h" +#include "lexbor/css/syntax/state_res.h" +#include "lexbor/css/syntax/token.h" + +#include "lexbor/core/array.h" + + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_lowercase[256]; +#endif + +static const lxb_char_t lxb_css_syntax_tokenizer_important[] = "important"; + + +static bool +lxb_css_syntax_tokenizer_lookup_important_ch(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch); + +static bool +lxb_css_syntax_tokenizer_lookup_important_end(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch); + +lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_create(void) +{ + return lexbor_calloc(1, sizeof(lxb_css_syntax_tokenizer_t)); +} + +lxb_status_t +lxb_css_syntax_tokenizer_init(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + static const unsigned tmp_size = 1024; + + if (tkz == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + /* Tokens. */ + + tkz->tokens = lexbor_dobject_create(); + status = lexbor_dobject_init(tkz->tokens, 128, + sizeof(lxb_css_syntax_token_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + /* Memory for text. */ + + tkz->mraw = lexbor_mraw_create(); + status = lexbor_mraw_init(tkz->mraw, 4096); + if (status != LXB_STATUS_OK) { + return status; + } + + /* Temp */ + tkz->start = lexbor_malloc(tmp_size); + if (tkz->start == NULL) { + return LXB_STATUS_ERROR_MEMORY_ALLOCATION; + } + + tkz->pos = tkz->start; + tkz->end = tkz->start + tmp_size; + + /* Parse errors */ + tkz->parse_errors = lexbor_array_obj_create(); + status = lexbor_array_obj_init(tkz->parse_errors, 16, + sizeof(lxb_css_syntax_tokenizer_error_t)); + if (status != LXB_STATUS_OK) { + return status; + } + + tkz->first = NULL; + tkz->last = NULL; + + tkz->offset = 0; + + tkz->with_comment = false; + tkz->with_unicode_range = false; + tkz->status = LXB_STATUS_OK; + tkz->opt = LXB_CSS_SYNTAX_TOKENIZER_OPT_UNDEF; + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_syntax_tokenizer_clean(lxb_css_syntax_tokenizer_t *tkz) +{ + lexbor_mraw_clean(tkz->mraw); + lexbor_array_obj_clean(tkz->parse_errors); + lexbor_dobject_clean(tkz->tokens); + + tkz->in_begin = NULL; + tkz->in_p = NULL; + tkz->in_end = NULL; + tkz->first = NULL; + tkz->last = NULL; + tkz->pos = tkz->start; + tkz->offset = 0; + tkz->status = LXB_STATUS_OK; + + return LXB_STATUS_OK; +} + +lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_destroy(lxb_css_syntax_tokenizer_t *tkz) +{ + if (tkz == NULL) { + return NULL; + } + + if (tkz->tokens != NULL) { + tkz->tokens = lexbor_dobject_destroy(tkz->tokens, true); + } + + tkz->mraw = lexbor_mraw_destroy(tkz->mraw, true); + tkz->parse_errors = lexbor_array_obj_destroy(tkz->parse_errors, true); + + if (tkz->start != NULL) { + tkz->start = lexbor_free(tkz->start); + } + + return lexbor_free(tkz); +} + +lxb_css_syntax_token_t * +lxb_css_syntax_tokenizer_token(lxb_css_syntax_tokenizer_t *tkz) +{ + lxb_status_t status; + lxb_css_syntax_token_t *token; + const lxb_char_t *begin, *end; + + begin = tkz->in_p; + end = tkz->in_end; + + token = lexbor_dobject_calloc(tkz->tokens); + if (token == NULL) { + tkz->status = LXB_STATUS_ERROR_MEMORY_ALLOCATION; + return NULL; + } + + if (tkz->first == NULL) { + tkz->first = token; + } + + if (tkz->last != NULL) { + status = lxb_css_syntax_token_string_make(tkz, tkz->last); + if (status != LXB_STATUS_OK) { + return NULL; + } + + tkz->last->next = token; + } + + tkz->last = token; + +again: + + lxb_css_syntax_token_base(token)->begin = begin; + + if (begin < end) { + begin = lxb_css_syntax_state_res_map[*begin](tkz, token, begin, end); + if (begin == NULL) { + return NULL; + } + } + else { + token->type = LXB_CSS_SYNTAX_TOKEN__EOF; + } + + token->offset = tkz->offset; + lxb_css_syntax_token_base(token)->length = begin - tkz->in_p; + tkz->offset += lxb_css_syntax_token_base(token)->length; + + tkz->in_p = begin; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_COMMENT && !tkz->with_comment) { + goto again; + } + + return token; +} + +bool +lxb_css_syntax_tokenizer_lookup_colon(lxb_css_syntax_tokenizer_t *tkz) +{ + const lxb_char_t *p, *end; + lxb_css_syntax_token_t *token; + + if (tkz->first != NULL && tkz->first->next != NULL) { + token = tkz->first->next; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + if (token->next != NULL) { + return token->next->type == LXB_CSS_SYNTAX_TOKEN_COLON; + } + } + else if (token->type == LXB_CSS_SYNTAX_TOKEN_COLON) { + return true; + } + + return false; + } + + p = tkz->in_p; + end = tkz->in_end; + + while (p < end) { + switch (*p) { + case 0x3A: + return true; + + case 0x0D: + case 0x0C: + case 0x09: + case 0x20: + case 0x0A: + p += 1; + break; + + default: + return false; + } + } + + return false; +} + +bool +lxb_css_syntax_tokenizer_lookup_important(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch) +{ + const lxb_char_t *p, *end; + lxb_css_syntax_token_t *token; + + static const size_t length = sizeof(lxb_css_syntax_tokenizer_important) - 1; + + p = tkz->in_p; + end = tkz->in_end; + + if (tkz->first != NULL && tkz->first->next != NULL) { + token = tkz->first->next; + + if (token->type != LXB_CSS_SYNTAX_TOKEN_IDENT) { + return false; + } + + if (!(lxb_css_syntax_token_ident(token)->length == length + && lexbor_str_data_ncasecmp(lxb_css_syntax_token_ident(token)->data, + lxb_css_syntax_tokenizer_important, + length))) + { + return false; + } + + if (token->next != NULL) { + token = token->next; + + if (token->type == LXB_CSS_SYNTAX_TOKEN_WHITESPACE) { + if (token->next == NULL) { + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, + p, end, stop_ch); + } + + token = token->next; + } + + return (token->type == LXB_CSS_SYNTAX_TOKEN_SEMICOLON + || token->type == stop + || token->type == LXB_CSS_SYNTAX_TOKEN__EOF); + } + + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, p, end, + stop_ch); + } + + return lxb_css_syntax_tokenizer_lookup_important_ch(tkz, p, end, stop_ch); +} + +static bool +lxb_css_syntax_tokenizer_lookup_important_ch(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch) +{ + static const size_t length = sizeof(lxb_css_syntax_tokenizer_important) - 1; + + if (!(lxb_size(end - p) >= length + && lexbor_str_data_ncasecmp(p, lxb_css_syntax_tokenizer_important, + length))) + { + return false; + } + + return lxb_css_syntax_tokenizer_lookup_important_end(tkz, p + length, + end, stop_ch); +} + +static bool +lxb_css_syntax_tokenizer_lookup_important_end(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *p, + const lxb_char_t *end, + const lxb_char_t stop_ch) +{ + while (p < end) { + switch (*p) { + case 0x3B: + return true; + + case 0x0D: + case 0x0C: + case 0x09: + case 0x20: + case 0x0A: + p += 1; + break; + + default: + return (stop_ch != 0x00 && stop_ch == *p); + } + } + + /* EOF */ + return true; +} + +bool +lxb_css_syntax_tokenizer_lookup_declaration_ws_end(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch) +{ + lxb_css_syntax_token_t *token; + const lxb_char_t *p, *end; + + if (tkz->first != NULL && tkz->first->next) { + token = tkz->first->next; + + switch (token->type) { + case LXB_CSS_SYNTAX_TOKEN_DELIM: + if (lxb_css_syntax_token_delim(token)->character != '!') { + return lxb_css_syntax_tokenizer_lookup_important(tkz, stop, + stop_ch); + } + + return false; + + case LXB_CSS_SYNTAX_TOKEN_SEMICOLON: + return true; + + default: + return token->type == stop_ch || + token->type == LXB_CSS_SYNTAX_TOKEN__EOF; + } + } + + p = tkz->in_p; + end = tkz->in_end; + + while (p < end) { + switch (*p) { + case 0x3B: + return true; + + case 0x21: + p += 1; + return lxb_css_syntax_tokenizer_lookup_important_ch(tkz, p, end, + stop_ch); + + default: + return (stop_ch != 0x00 && stop_ch == *p); + } + } + + return false; +} + +/* + * No inline functions for ABI. + */ +lxb_status_t +lxb_css_syntax_tokenizer_status_noi(lxb_css_syntax_tokenizer_t *tkz) +{ + return lxb_css_syntax_tokenizer_status(tkz); +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.h new file mode 100644 index 0000000..c72ed76 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_TOKENIZER_H +#define LEXBOR_CSS_SYNTAX_TOKENIZER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/array_obj.h" + +#include "lexbor/css/syntax/base.h" +#include "lexbor/css/syntax/token.h" + + +/* State */ +typedef const lxb_char_t * +(*lxb_css_syntax_tokenizer_state_f)(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_t *token, + const lxb_char_t *data, const lxb_char_t *end); + +enum lxb_css_syntax_tokenizer_opt { + LXB_CSS_SYNTAX_TOKENIZER_OPT_UNDEF = 0x00, +}; + +struct lxb_css_syntax_tokenizer { + lexbor_dobject_t *tokens; + lexbor_array_obj_t *parse_errors; + lexbor_mraw_t *mraw; + + lxb_css_syntax_token_t *first; + lxb_css_syntax_token_t *last; + + const lxb_char_t *in_begin; + const lxb_char_t *in_end; + const lxb_char_t *in_p; + + /* Temp */ + lxb_char_t *start; + lxb_char_t *pos; + const lxb_char_t *end; + + size_t offset; + + /* Process */ + unsigned int opt; /* bitmap */ + lxb_status_t status; + bool with_comment; + bool with_unicode_range; +}; + + +LXB_API lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_create(void); + +LXB_API lxb_status_t +lxb_css_syntax_tokenizer_init(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API lxb_status_t +lxb_css_syntax_tokenizer_clean(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API lxb_css_syntax_tokenizer_t * +lxb_css_syntax_tokenizer_destroy(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API lxb_status_t +lxb_css_syntax_tokenizer_next_chunk(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t **data, const lxb_char_t **end); + +LXB_API bool +lxb_css_syntax_tokenizer_lookup_colon(lxb_css_syntax_tokenizer_t *tkz); + +LXB_API bool +lxb_css_syntax_tokenizer_lookup_important(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch); + +LXB_API bool +lxb_css_syntax_tokenizer_lookup_declaration_ws_end(lxb_css_syntax_tokenizer_t *tkz, + lxb_css_syntax_token_type_t stop, + const lxb_char_t stop_ch); + +/* + * Inline functions + */ +lxb_inline lxb_status_t +lxb_css_syntax_tokenizer_status(lxb_css_syntax_tokenizer_t *tkz) +{ + return tkz->status; +} + +lxb_inline void +lxb_css_syntax_tokenizer_buffer_set(lxb_css_syntax_tokenizer_t *tkz, + const lxb_char_t *data, size_t size) +{ + tkz->in_begin = data; + tkz->in_p = data; + tkz->in_end = data + size; +} + +lxb_inline void +lxb_css_syntax_tokenizer_with_unicode_range(lxb_css_syntax_tokenizer_t *tkz, + bool with_range) +{ + tkz->with_unicode_range = with_range; +} + + +/* + * No inline functions for ABI. + */ +LXB_API lxb_status_t +lxb_css_syntax_tokenizer_status_noi(lxb_css_syntax_tokenizer_t *tkz); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_TOKENIZER_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.c new file mode 100644 index 0000000..a95723c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.c @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2018-2019 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/syntax/tokenizer/error.h" + + +lxb_css_syntax_tokenizer_error_t * +lxb_css_syntax_tokenizer_error_add(lexbor_array_obj_t *parse_errors, + const lxb_char_t *pos, + lxb_css_syntax_tokenizer_error_id_t id) +{ + if (parse_errors == NULL) { + return NULL; + } + + lxb_css_syntax_tokenizer_error_t *entry; + + entry = lexbor_array_obj_push(parse_errors); + if (entry == NULL) { + return NULL; + } + + entry->id = id; + entry->pos = pos; + + return entry; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.h new file mode 100644 index 0000000..66a1e17 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/syntax/tokenizer/error.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018-2025 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LEXBOR_CSS_SYNTAX_TOKENIZER_ERROR_H +#define LEXBOR_CSS_SYNTAX_TOKENIZER_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/core/base.h" +#include "lexbor/core/array_obj.h" + + +typedef enum { + /* unexpected-eof */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_UNEOF = 0x0000, + /* eof-in-comment */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINCO, + /* eof-in-string */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINST, + /* eof-in-url */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINUR, + /* eof-in-escaped */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_EOINES, + /* qo-in-url */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_QOINUR, + /* wrong-escape-in-url */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_WRESINUR, + /* newline-in-string */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_NEINST, + /* bad-char */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_BACH, + /* bad-code-point */ + LXB_CSS_SYNTAX_TOKENIZER_ERROR_BACOPO, +} +lxb_css_syntax_tokenizer_error_id_t; + +typedef struct { + const lxb_char_t *pos; + lxb_css_syntax_tokenizer_error_id_t id; +} +lxb_css_syntax_tokenizer_error_t; + + +LXB_API lxb_css_syntax_tokenizer_error_t * +lxb_css_syntax_tokenizer_error_add(lexbor_array_obj_t *parse_errors, + const lxb_char_t *pos, + lxb_css_syntax_tokenizer_error_id_t id); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LEXBOR_CSS_SYNTAX_TOKENIZER_ERROR_H */ + diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.c new file mode 100644 index 0000000..f3cc9c8 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.c @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/unit/res.h" + + +const lxb_css_data_t * +lxb_css_unit_absolute_relative_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_absolute_relative_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_absolute_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_absolute_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_relative_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_relative_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_angle_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_angle_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_frequency_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_frequency_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_resolution_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_resolution_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_duration_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_unit_duration_shs, + name, length); + if (entry == NULL) { + return NULL; + } + + return entry->value; +} + +const lxb_css_data_t * +lxb_css_unit_by_id(lxb_css_type_t id) +{ + return &lxb_css_unit_data[id]; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.h new file mode 100644 index 0000000..06dba0d --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2021 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_UNIT_H +#define LXB_CSS_UNIT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" + + +LXB_API const lxb_css_data_t * +lxb_css_unit_absolute_relative_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_absolute_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_relative_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_angle_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_frequency_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_resolution_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_duration_by_name(const lxb_char_t *name, size_t length); + +LXB_API const lxb_css_data_t * +lxb_css_unit_by_id(lxb_css_type_t id); + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_UNIT_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/const.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/const.h new file mode 100644 index 0000000..9f5e515 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/const.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_UNIT_CONST_H +#define LXB_CSS_UNIT_CONST_H + + +#include "lexbor/css/value/const.h" + + +typedef enum { + LXB_CSS_UNIT__UNDEF = 0x0000, + LXB_CSS_UNIT__LAST_ENTRY = 0x0022 +} +lxb_css_unit_t; + +typedef enum { + LXB_CSS_UNIT_ABSOLUTE__BEGIN = 0x0001, + LXB_CSS_UNIT_Q = 0x0001, + LXB_CSS_UNIT_CM = 0x0002, + LXB_CSS_UNIT_IN = 0x0003, + LXB_CSS_UNIT_MM = 0x0004, + LXB_CSS_UNIT_PC = 0x0005, + LXB_CSS_UNIT_PT = 0x0006, + LXB_CSS_UNIT_PX = 0x0007, + LXB_CSS_UNIT_ABSOLUTE__LAST_ENTRY = 0x0008 +} +lxb_css_unit_absolute_t; + +typedef enum { + LXB_CSS_UNIT_RELATIVE__BEGIN = 0x0008, + LXB_CSS_UNIT_CAP = 0x0008, + LXB_CSS_UNIT_CH = 0x0009, + LXB_CSS_UNIT_EM = 0x000a, + LXB_CSS_UNIT_EX = 0x000b, + LXB_CSS_UNIT_IC = 0x000c, + LXB_CSS_UNIT_LH = 0x000d, + LXB_CSS_UNIT_REM = 0x000e, + LXB_CSS_UNIT_RLH = 0x000f, + LXB_CSS_UNIT_VB = 0x0010, + LXB_CSS_UNIT_VH = 0x0011, + LXB_CSS_UNIT_VI = 0x0012, + LXB_CSS_UNIT_VMAX = 0x0013, + LXB_CSS_UNIT_VMIN = 0x0014, + LXB_CSS_UNIT_VW = 0x0015, + LXB_CSS_UNIT_RELATIVE__LAST_ENTRY = 0x0016 +} +lxb_css_unit_relative_t; + +typedef enum { + LXB_CSS_UNIT_ANGLE__BEGIN = 0x0016, + LXB_CSS_UNIT_DEG = 0x0016, + LXB_CSS_UNIT_GRAD = 0x0017, + LXB_CSS_UNIT_RAD = 0x0018, + LXB_CSS_UNIT_TURN = 0x0019, + LXB_CSS_UNIT_ANGLE__LAST_ENTRY = 0x001a +} +lxb_css_unit_angle_t; + +typedef enum { + LXB_CSS_UNIT_FREQUENCY__BEGIN = 0x001a, + LXB_CSS_UNIT_HZ = 0x001a, + LXB_CSS_UNIT_KHZ = 0x001b, + LXB_CSS_UNIT_FREQUENCY__LAST_ENTRY = 0x001c +} +lxb_css_unit_frequency_t; + +typedef enum { + LXB_CSS_UNIT_RESOLUTION__BEGIN = 0x001c, + LXB_CSS_UNIT_DPCM = 0x001c, + LXB_CSS_UNIT_DPI = 0x001d, + LXB_CSS_UNIT_DPPX = 0x001e, + LXB_CSS_UNIT_X = 0x001f, + LXB_CSS_UNIT_RESOLUTION__LAST_ENTRY = 0x0020 +} +lxb_css_unit_resolution_t; + +typedef enum { + LXB_CSS_UNIT_DURATION__BEGIN = 0x0020, + LXB_CSS_UNIT_MS = 0x0020, + LXB_CSS_UNIT_S = 0x0021, + LXB_CSS_UNIT_DURATION__LAST_ENTRY = 0x0022 +} +lxb_css_unit_duration_t; + + + +#endif /* LXB_CSS_UNIT_CONST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/res.h new file mode 100644 index 0000000..ac24503 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/unit/res.h @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_UNIT_RES_H +#define LXB_CSS_UNIT_RES_H + +#include "lexbor/core/shs.h" +#include "lexbor/css/unit/const.h" + + +static const lxb_css_data_t lxb_css_unit_data[LXB_CSS_UNIT__LAST_ENTRY] = +{ + {(lxb_char_t *) "#undef", 6, LXB_CSS_UNIT__UNDEF}, + {(lxb_char_t *) "Q", 1, LXB_CSS_UNIT_Q}, + {(lxb_char_t *) "cm", 2, LXB_CSS_UNIT_CM}, + {(lxb_char_t *) "in", 2, LXB_CSS_UNIT_IN}, + {(lxb_char_t *) "mm", 2, LXB_CSS_UNIT_MM}, + {(lxb_char_t *) "pc", 2, LXB_CSS_UNIT_PC}, + {(lxb_char_t *) "pt", 2, LXB_CSS_UNIT_PT}, + {(lxb_char_t *) "px", 2, LXB_CSS_UNIT_PX}, + {(lxb_char_t *) "cap", 3, LXB_CSS_UNIT_CAP}, + {(lxb_char_t *) "ch", 2, LXB_CSS_UNIT_CH}, + {(lxb_char_t *) "em", 2, LXB_CSS_UNIT_EM}, + {(lxb_char_t *) "ex", 2, LXB_CSS_UNIT_EX}, + {(lxb_char_t *) "ic", 2, LXB_CSS_UNIT_IC}, + {(lxb_char_t *) "lh", 2, LXB_CSS_UNIT_LH}, + {(lxb_char_t *) "rem", 3, LXB_CSS_UNIT_REM}, + {(lxb_char_t *) "rlh", 3, LXB_CSS_UNIT_RLH}, + {(lxb_char_t *) "vb", 2, LXB_CSS_UNIT_VB}, + {(lxb_char_t *) "vh", 2, LXB_CSS_UNIT_VH}, + {(lxb_char_t *) "vi", 2, LXB_CSS_UNIT_VI}, + {(lxb_char_t *) "vmax", 4, LXB_CSS_UNIT_VMAX}, + {(lxb_char_t *) "vmin", 4, LXB_CSS_UNIT_VMIN}, + {(lxb_char_t *) "vw", 2, LXB_CSS_UNIT_VW}, + {(lxb_char_t *) "deg", 3, LXB_CSS_UNIT_DEG}, + {(lxb_char_t *) "grad", 4, LXB_CSS_UNIT_GRAD}, + {(lxb_char_t *) "rad", 3, LXB_CSS_UNIT_RAD}, + {(lxb_char_t *) "turn", 4, LXB_CSS_UNIT_TURN}, + {(lxb_char_t *) "Hz", 2, LXB_CSS_UNIT_HZ}, + {(lxb_char_t *) "kHz", 3, LXB_CSS_UNIT_KHZ}, + {(lxb_char_t *) "dpcm", 4, LXB_CSS_UNIT_DPCM}, + {(lxb_char_t *) "dpi", 3, LXB_CSS_UNIT_DPI}, + {(lxb_char_t *) "dppx", 4, LXB_CSS_UNIT_DPPX}, + {(lxb_char_t *) "x", 1, LXB_CSS_UNIT_X}, + {(lxb_char_t *) "ms", 2, LXB_CSS_UNIT_MS}, + {(lxb_char_t *) "s", 1, LXB_CSS_UNIT_S} +}; + +static const lexbor_shs_entry_t lxb_css_unit_absolute_relative_shs[84] = +{ + {NULL, NULL, 83, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"rem", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_REM], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vmax", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VMAX], 4, 0}, + {"pc", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PC], 2, 0}, + {"lh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_LH], 2, 0}, + {NULL, NULL, 0, 0}, + {"rlh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_RLH], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"cm", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CM], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"q", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_Q], 1, 0}, + {"ic", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_IC], 2, 0}, + {NULL, NULL, 0, 0}, + {"vw", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VW], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"pt", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PT], 2, 0}, + {"vb", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VB], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"ex", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_EX], 2, 0}, + {"in", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_IN], 2, 0}, + {"cap", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CAP], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"em", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_EM], 2, 0}, + {NULL, NULL, 0, 0}, + {"vmin", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VMIN], 4, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vi", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VI], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"mm", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_MM], 2, 0}, + {"ch", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CH], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"px", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PX], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VH], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_absolute_shs[30] = +{ + {NULL, NULL, 29, 0}, + {NULL, NULL, 0, 0}, + {"in", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_IN], 2, 0}, + {"pt", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PT], 2, 0}, + {"q", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_Q], 1, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"mm", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_MM], 2, 0}, + {"px", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PX], 2, 0}, + {"cm", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CM], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"pc", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_PC], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_relative_shs[64] = +{ + {NULL, NULL, 63, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"ic", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_IC], 2, 0}, + {"cap", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CAP], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"rem", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_REM], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"em", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_EM], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"ch", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_CH], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"ex", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_EX], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vb", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VB], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VH], 2, 0}, + {"vi", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VI], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"rlh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_RLH], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vmin", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VMIN], 4, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"lh", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_LH], 2, 0}, + {NULL, NULL, 0, 0}, + {"vw", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VW], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"vmax", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_VMAX], 4, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_angle_shs[7] = +{ + {NULL, NULL, 6, 0}, + {"turn", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_TURN], 4, 0}, + {"deg", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_DEG], 3, 0}, + {"grad", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_GRAD], 4, 0}, + {"rad", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_RAD], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_frequency_shs[6] = +{ + {NULL, NULL, 5, 0}, + {NULL, NULL, 0, 0}, + {"khz", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_KHZ], 3, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0}, + {"hz", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_HZ], 2, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_resolution_shs[7] = +{ + {NULL, NULL, 6, 0}, + {NULL, NULL, 0, 0}, + {"x", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_X], 1, 0}, + {"dpcm", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_DPCM], 4, 0}, + {"dpi", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_DPI], 3, 0}, + {"dppx", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_DPPX], 4, 0}, + {NULL, NULL, 0, 0} +}; + +static const lexbor_shs_entry_t lxb_css_unit_duration_shs[6] = +{ + {NULL, NULL, 5, 0}, + {NULL, NULL, 0, 0}, + {"s", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_S], 1, 0}, + {"ms", (void *) &lxb_css_unit_data[LXB_CSS_UNIT_MS], 2, 0}, + {NULL, NULL, 0, 0}, + {NULL, NULL, 0, 0} +}; + + + + +#endif /* LXB_CSS_UNIT_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.c new file mode 100644 index 0000000..23e2472 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.c @@ -0,0 +1,678 @@ +/* + * Copyright (C) 2022-2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/css/css.h" +#include "lexbor/css/value/res.h" + +#include "lexbor/core/serialize.h" +#include "lexbor/core/conv.h" + + +#ifndef LEXBOR_DISABLE_INTERNAL_EXTERN + LXB_EXTERN const lxb_char_t lexbor_str_res_map_hex_to_char_lowercase[17]; + LXB_EXTERN const char *lexbor_str_res_char_to_two_hex_value_lowercase[257]; +#endif + +static const lexbor_str_t lxb_css_value_str_ws = lexbor_str(" "); +static const lexbor_str_t lxb_str_comma = lexbor_str(", "); +static const lexbor_str_t lxb_str_alpha = lexbor_str(" / "); +static const lexbor_str_t lxb_str_rp = lexbor_str(")"); + + +const lxb_css_data_t * +lxb_css_value_by_id(uintptr_t id) +{ + if (id < LXB_CSS_VALUE__LAST_ENTRY) { + return &lxb_css_value_data[id]; + } + + return NULL; +} + +lxb_css_value_type_t +lxb_css_value_by_name(const lxb_char_t *name, size_t length) +{ + const lexbor_shs_entry_t *entry; + + entry = lexbor_shs_entry_get_lower_static(lxb_css_value_shs, name, length); + if (entry == NULL) { + return LXB_CSS_VALUE__UNDEF; + } + + return (lxb_css_value_type_t) (uintptr_t) entry->value; +} + +lxb_status_t +lxb_css_value_serialize(lxb_css_value_type_t type, + lexbor_serialize_cb_f cb, void *ctx) +{ + const lxb_css_data_t *data; + + if (type >= LXB_CSS_VALUE__LAST_ENTRY) { + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + data = &lxb_css_value_data[type]; + + return cb(data->name, data->length, ctx); +} + +lxb_status_t +lxb_css_value_percentage_sr(const lxb_css_value_percentage_t *percent, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t buf[128]; + lxb_status_t status; + + static const lexbor_str_t str_per = lexbor_str("%"); + + /* FIXME: If length != sizeof(buf)? */ + length = lexbor_conv_float_to_data(percent->num, buf, sizeof(buf)); + + lexbor_serialize_write(cb, buf, length, ctx, status); + lexbor_serialize_write(cb, str_per.data, str_per.length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_length_sr(const lxb_css_value_length_t *len, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t buf[128]; + lxb_status_t status; + const lxb_css_data_t *unit; + + /* FIXME: If length != sizeof(buf)? */ + length = lexbor_conv_float_to_data(len->num, buf, sizeof(buf)); + + lexbor_serialize_write(cb, buf, length, ctx, status); + + if (len->unit == LXB_CSS_UNIT__UNDEF) { + return LXB_STATUS_OK; + } + + unit = lxb_css_unit_by_id(len->unit); + if (unit == NULL) { + return LXB_STATUS_OK; + } + + lexbor_serialize_write(cb, unit->name, unit->length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_number_sr(const lxb_css_value_number_t *number, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t buf[128]; + lxb_status_t status; + + /* FIXME: If length != sizeof(buf)? */ + length = lexbor_conv_float_to_data(number->num, buf, sizeof(buf)); + + lexbor_serialize_write(cb, buf, length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_integer_sr(const lxb_css_value_integer_t *integer, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t buf[128]; + lxb_status_t status; + + /* FIXME: If length != sizeof(buf)? */ + length = lexbor_conv_long_to_data(integer->num, buf, sizeof(buf)); + + lexbor_serialize_write(cb, buf, length, ctx, status); + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_length_percentage_sr(const lxb_css_value_length_percentage_t *lp, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (lp->type) { + case LXB_CSS_VALUE__LENGTH: + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_length_sr(&lp->u.length, cb, ctx); + + case LXB_CSS_VALUE__PERCENTAGE: + return lxb_css_value_percentage_sr(&lp->u.percentage, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(lp->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_number_length_sr(const lxb_css_value_number_length_t *nl, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (nl->type) { + case LXB_CSS_VALUE__LENGTH: + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_length_sr(&nl->u.length, cb, ctx); + + default: + return lxb_css_value_serialize(nl->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_number_percentage_sr(const lxb_css_value_number_percentage_t *np, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (np->type) { + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_number_sr(&np->u.number, cb, ctx); + + case LXB_CSS_VALUE__PERCENTAGE: + return lxb_css_value_percentage_sr(&np->u.percentage, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(np->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_length_type_sr(const lxb_css_value_length_type_t *lt, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (lt->type) { + case LXB_CSS_VALUE__LENGTH: + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_length_sr(<->length, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(lt->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_percentage_type_sr(const lxb_css_value_percentage_type_t *pt, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (pt->type) { + case LXB_CSS_VALUE__PERCENTAGE: + return lxb_css_value_percentage_sr(&pt->percentage, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(pt->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_number_type_sr(const lxb_css_value_number_type_t *num, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (num->type) { + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_number_sr(&num->number, cb, ctx); + + default: + return lxb_css_value_serialize(num->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_integer_type_sr(const lxb_css_value_integer_type_t *num, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (num->type) { + case LXB_CSS_VALUE__INTEGER: + return lxb_css_value_integer_sr(&num->integer, cb, ctx); + + default: + return lxb_css_value_serialize(num->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_length_percentage_type_sr(const lxb_css_value_length_percentage_type_t *lpt, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (lpt->type) { + case LXB_CSS_VALUE__LENGTH: + return lxb_css_value_length_percentage_sr(&lpt->length, cb, ctx); + + default: + return lxb_css_value_serialize(lpt->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_number_length_percentage_type_sr(const lxb_css_value_number_length_percentage_t *nlp, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (nlp->type) { + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_number_sr(&nlp->u.number, cb, ctx); + + case LXB_CSS_VALUE__LENGTH: + return lxb_css_value_length_sr(&nlp->u.length, cb, ctx); + + case LXB_CSS_VALUE__PERCENTAGE: + return lxb_css_value_percentage_sr(&nlp->u.percentage, cb, ctx); + + default: + return lxb_css_value_serialize(nlp->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +lxb_status_t +lxb_css_value_angle_sr(const lxb_css_value_angle_t *angle, + lexbor_serialize_cb_f cb, void *ctx) +{ + size_t length; + lxb_char_t buf[128]; + lxb_status_t status; + const lxb_css_data_t *data; + + /* FIXME: If length != sizeof(buf)? */ + length = lexbor_conv_float_to_data(angle->num, buf, sizeof(buf)); + + lexbor_serialize_write(cb, buf, length, ctx, status); + + data = lxb_css_unit_by_id(angle->unit); + + return cb(data->name, data->length, ctx); +} + +lxb_status_t +lxb_css_value_hue_sr(const lxb_css_value_hue_t *hue, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (hue->type) { + case LXB_CSS_VALUE__NUMBER: + return lxb_css_value_number_sr(&hue->u.number, cb, ctx); + + case LXB_CSS_VALUE__ANGLE: + return lxb_css_value_angle_sr(&hue->u.angle, cb, ctx); + + case LXB_CSS_VALUE__UNDEF: + /* FIXME: ???? */ + break; + + default: + return lxb_css_value_serialize(hue->type, cb, ctx); + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_value_color_hex_sr(const lxb_css_value_color_hex_t *hex, + lexbor_serialize_cb_f cb, void *ctx) +{ + lxb_status_t status; + static const lexbor_str_t str_hash = lexbor_str("#"); + const lxb_css_value_color_hex_rgba_t *rgba = &hex->rgba; + + const lxb_char_t *hmo = lexbor_str_res_map_hex_to_char_lowercase; + const char **hmt = lexbor_str_res_char_to_two_hex_value_lowercase; + + lexbor_serialize_write(cb, str_hash.data, str_hash.length, ctx, status); + + switch (hex->type) { + case LXB_CSS_PROPERTY_COLOR_HEX_TYPE_3: + case LXB_CSS_PROPERTY_COLOR_HEX_TYPE_4: + lexbor_serialize_write(cb, &hmo[rgba->r], 1, ctx, status); + lexbor_serialize_write(cb, &hmo[rgba->g], 1, ctx, status); + lexbor_serialize_write(cb, &hmo[rgba->b], 1, ctx, status); + + if (hex->type == LXB_CSS_PROPERTY_COLOR_HEX_TYPE_4) { + lexbor_serialize_write(cb, &hmo[rgba->a], 1, ctx, status); + } + + break; + + case LXB_CSS_PROPERTY_COLOR_HEX_TYPE_6: + case LXB_CSS_PROPERTY_COLOR_HEX_TYPE_8: + lexbor_serialize_write(cb, hmt[rgba->r], 2, ctx, status); + lexbor_serialize_write(cb, hmt[rgba->g], 2, ctx, status); + lexbor_serialize_write(cb, hmt[rgba->b], 2, ctx, status); + + if (hex->type == LXB_CSS_PROPERTY_COLOR_HEX_TYPE_8) { + lexbor_serialize_write(cb, hmt[rgba->a], 2, ctx, status); + } + + break; + + default: + break; + } + + return LXB_STATUS_OK; +} + +static lxb_status_t +lxb_css_value_color_rgb_sr(const lxb_css_value_color_rgba_t *rgb, + lexbor_serialize_cb_f cb, void *ctx, + lxb_css_value_type_t type) +{ + lxb_status_t status; + const lexbor_str_t *sep; + static const lexbor_str_t str_rgb = lexbor_str("rgb("); + static const lexbor_str_t str_rgba = lexbor_str("rgba("); + + if (type == LXB_CSS_COLOR_RGB) { + lexbor_serialize_write(cb, str_rgb.data, str_rgb.length, ctx, status); + } + else { + lexbor_serialize_write(cb, str_rgba.data, str_rgba.length, ctx, status); + } + + sep = (rgb->old) ? &lxb_str_comma : &lxb_css_value_str_ws; + + status = lxb_css_value_number_percentage_sr(&rgb->r, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&rgb->g, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&rgb->b, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (rgb->a.type == LXB_CSS_VALUE__UNDEF) { + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); + } + + sep = (rgb->old) ? &lxb_str_comma : &lxb_str_alpha; + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&rgb->a, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); +} + +static lxb_status_t +lxb_css_value_color_hsl_sr(const lxb_css_value_color_hsla_t *hsl, + lexbor_serialize_cb_f cb, void *ctx, + lxb_css_value_type_t type) +{ + lxb_status_t status; + const lexbor_str_t *sep; + static const lexbor_str_t str_hsl = lexbor_str("hsl("); + static const lexbor_str_t str_hsla = lexbor_str("hsla("); + static const lexbor_str_t str_hwb = lexbor_str("hwb("); + + switch (type) { + case LXB_CSS_COLOR_HSL: + status = cb(str_hsl.data, str_hsl.length, ctx); + break; + + case LXB_CSS_COLOR_HSLA: + status = cb(str_hsla.data, str_hsla.length, ctx); + break; + + case LXB_CSS_COLOR_HWB: + status = cb(str_hwb.data, str_hwb.length, ctx); + break; + + default: + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + if (status != LXB_STATUS_OK) { + return status; + } + + sep = (hsl->old) ? &lxb_str_comma : &lxb_css_value_str_ws; + + status = lxb_css_value_hue_sr(&hsl->h, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_percentage_type_sr(&hsl->s, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_percentage_type_sr(&hsl->l, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (hsl->a.type == LXB_CSS_VALUE__UNDEF) { + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); + } + + sep = (hsl->old) ? &lxb_str_comma : &lxb_str_alpha; + + lexbor_serialize_write(cb, sep->data, sep->length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&hsl->a, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); +} + +static lxb_status_t +lxb_css_value_color_lab_sr(const lxb_css_value_color_lab_t *lab, + lexbor_serialize_cb_f cb, void *ctx, + lxb_css_value_type_t type) +{ + lxb_status_t status; + static const lexbor_str_t str_lab = lexbor_str("lab("); + static const lexbor_str_t str_oklab = lexbor_str("oklab("); + + switch (type) { + case LXB_CSS_COLOR_LAB: + status = cb(str_lab.data, str_lab.length, ctx); + break; + + case LXB_CSS_COLOR_OKLAB: + status = cb(str_oklab.data, str_oklab.length, ctx); + break; + + default: + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + if (status != LXB_STATUS_OK) { + return status; + } + + status = lxb_css_value_number_percentage_sr(&lab->l, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, lxb_css_value_str_ws.data, + lxb_css_value_str_ws.length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&lab->a, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, lxb_css_value_str_ws.data, + lxb_css_value_str_ws.length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&lab->b, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (lab->alpha.type == LXB_CSS_VALUE__UNDEF) { + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); + } + + lexbor_serialize_write(cb, lxb_str_alpha.data, lxb_str_alpha.length, + ctx, status); + + status = lxb_css_value_number_percentage_sr(&lab->alpha, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); +} + +static lxb_status_t +lxb_css_value_color_lch_sr(const lxb_css_value_color_lch_t *lch, + lexbor_serialize_cb_f cb, void *ctx, + lxb_css_value_type_t type) +{ + lxb_status_t status; + static const lexbor_str_t str_lch = lexbor_str("lch("); + static const lexbor_str_t str_oklch = lexbor_str("oklch("); + + switch (type) { + case LXB_CSS_COLOR_LCH: + status = cb(str_lch.data, str_lch.length, ctx); + break; + + case LXB_CSS_COLOR_OKLCH: + status = cb(str_oklch.data, str_oklch.length, ctx); + break; + + default: + return LXB_STATUS_ERROR_WRONG_ARGS; + } + + if (status != LXB_STATUS_OK) { + return status; + } + + status = lxb_css_value_number_percentage_sr(&lch->l, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, lxb_css_value_str_ws.data, + lxb_css_value_str_ws.length, ctx, status); + + status = lxb_css_value_number_percentage_sr(&lch->c, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + lexbor_serialize_write(cb, lxb_css_value_str_ws.data, + lxb_css_value_str_ws.length, ctx, status); + + status = lxb_css_value_hue_sr(&lch->h, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + if (lch->a.type == LXB_CSS_VALUE__UNDEF) { + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); + } + + lexbor_serialize_write(cb, lxb_str_alpha.data, lxb_str_alpha.length, + ctx, status); + + status = lxb_css_value_number_percentage_sr(&lch->a, cb, ctx); + if (status != LXB_STATUS_OK) { + return status; + } + + return cb(lxb_str_rp.data, lxb_str_rp.length, ctx); +} + +lxb_status_t +lxb_css_value_color_serialize(const lxb_css_value_color_t *color, + lexbor_serialize_cb_f cb, void *ctx) +{ + switch (color->type) { + case LXB_CSS_COLOR_HEX: + return lxb_css_value_color_hex_sr(&color->u.hex, cb, ctx); + + case LXB_CSS_COLOR_RGB: + case LXB_CSS_COLOR_RGBA: + return lxb_css_value_color_rgb_sr(&color->u.rgb, cb, ctx, + color->type); + + case LXB_CSS_COLOR_HSL: + case LXB_CSS_COLOR_HSLA: + case LXB_CSS_COLOR_HWB: + return lxb_css_value_color_hsl_sr(&color->u.hsl, cb, ctx, + color->type); + + case LXB_CSS_COLOR_LAB: + case LXB_CSS_COLOR_OKLAB: + return lxb_css_value_color_lab_sr(&color->u.lab, cb, ctx, + color->type); + + case LXB_CSS_COLOR_LCH: + case LXB_CSS_COLOR_OKLCH: + return lxb_css_value_color_lch_sr(&color->u.lch, cb, ctx, + color->type); + + case LXB_CSS_VALUE__UNDEF: + break; + + default: + return lxb_css_value_serialize(color->type, cb, ctx); + } + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.h new file mode 100644 index 0000000..eb26aa8 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2022-2023 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#ifndef LXB_CSS_VALUE_H +#define LXB_CSS_VALUE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "lexbor/css/base.h" +#include "lexbor/css/value/const.h" +#include "lexbor/css/unit/const.h" + +typedef struct { + double num; + bool is_float; +} +lxb_css_value_number_t; + +typedef struct { + long num; +} +lxb_css_value_integer_t; + +typedef lxb_css_value_number_t lxb_css_value_percentage_t; + +typedef struct { + double num; + bool is_float; + lxb_css_unit_t unit; +} +lxb_css_value_length_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_length_t length; + lxb_css_value_percentage_t percentage; + } u; +} +lxb_css_value_length_percentage_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_number_t number; + lxb_css_value_length_t length; + lxb_css_value_percentage_t percentage; + } u; +} +lxb_css_value_number_length_percentage_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_number_t number; + lxb_css_value_length_t length; + } u; +} +lxb_css_value_number_length_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_number_t number; + lxb_css_value_percentage_t percentage; + } u; +} +lxb_css_value_number_percentage_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_number_t number; +} +lxb_css_value_number_type_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_integer_t integer; +} +lxb_css_value_integer_type_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_percentage_t percentage; +} +lxb_css_value_percentage_type_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_length_t length; +} +lxb_css_value_length_type_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_length_percentage_t length; +} +lxb_css_value_length_percentage_type_t; + +typedef struct { + double num; + bool is_float; + lxb_css_unit_angle_t unit; +} +lxb_css_value_angle_t; + +typedef struct { + lxb_css_value_type_t type; + lxb_css_value_angle_t angle; +} +lxb_css_value_angle_type_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_number_t number; + lxb_css_value_angle_t angle; + } u; +} +lxb_css_value_hue_t; + +typedef struct { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; +} +lxb_css_value_color_hex_rgba_t; + +typedef enum { + LXB_CSS_PROPERTY_COLOR_HEX_TYPE_3 = 0x00, + LXB_CSS_PROPERTY_COLOR_HEX_TYPE_4, + LXB_CSS_PROPERTY_COLOR_HEX_TYPE_6, + LXB_CSS_PROPERTY_COLOR_HEX_TYPE_8 +} +lxb_css_value_color_hex_type_t; + +typedef struct { + lxb_css_value_color_hex_rgba_t rgba; + lxb_css_value_color_hex_type_t type; +} +lxb_css_value_color_hex_t; + +typedef struct { + /* + * If R is when G and B should be to. + * If R is when G and B should be to. + * R, G, B can be NONE regardless of neighboring values. + * 'A' can be or or NONE. + */ + + lxb_css_value_number_percentage_t r; + lxb_css_value_number_percentage_t g; + lxb_css_value_number_percentage_t b; + lxb_css_value_number_percentage_t a; + + bool old; +} +lxb_css_value_color_rgba_t; + +typedef struct { + lxb_css_value_hue_t h; + lxb_css_value_percentage_type_t s; + lxb_css_value_percentage_type_t l; + lxb_css_value_number_percentage_t a; + + bool old; +} +lxb_css_value_color_hsla_t; + +typedef struct { + lxb_css_value_number_percentage_t l; + lxb_css_value_number_percentage_t a; + lxb_css_value_number_percentage_t b; + lxb_css_value_number_percentage_t alpha; +} +lxb_css_value_color_lab_t; + +typedef struct { + lxb_css_value_number_percentage_t l; + lxb_css_value_number_percentage_t c; + lxb_css_value_hue_t h; + lxb_css_value_number_percentage_t a; +} +lxb_css_value_color_lch_t; + +typedef struct { + lxb_css_value_type_t type; + + union { + lxb_css_value_color_hex_t hex; + lxb_css_value_color_rgba_t rgb; + lxb_css_value_color_hsla_t hsl; + lxb_css_value_color_hsla_t hwb; + lxb_css_value_color_lab_t lab; + lxb_css_value_color_lch_t lch; + } u; +} +lxb_css_value_color_t; + + +LXB_API const lxb_css_data_t * +lxb_css_value_by_id(uintptr_t id); + +LXB_API lxb_css_value_type_t +lxb_css_value_by_name(const lxb_char_t *name, size_t length); + +LXB_API lxb_status_t +lxb_css_value_serialize(lxb_css_value_type_t type, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_percentage_sr(const lxb_css_value_percentage_t *percent, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_length_sr(const lxb_css_value_length_t *len, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_number_sr(const lxb_css_value_number_t *number, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_integer_sr(const lxb_css_value_integer_t *integer, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_length_percentage_sr(const lxb_css_value_length_percentage_t *lp, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_number_length_sr(const lxb_css_value_number_length_t *nl, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_number_percentage_sr(const lxb_css_value_number_percentage_t *np, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_length_type_sr(const lxb_css_value_length_type_t *lt, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_percentage_type_sr(const lxb_css_value_percentage_type_t *pt, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_number_type_sr(const lxb_css_value_number_type_t *num, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_integer_type_sr(const lxb_css_value_integer_type_t *num, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_length_percentage_type_sr(const lxb_css_value_length_percentage_type_t *lpt, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_number_length_percentage_type_sr(const lxb_css_value_number_length_percentage_t *nlp, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_angle_sr(const lxb_css_value_angle_t *angle, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_hue_sr(const lxb_css_value_hue_t *hue, + lexbor_serialize_cb_f cb, void *ctx); + +LXB_API lxb_status_t +lxb_css_value_color_serialize(const lxb_css_value_color_t *color, + lexbor_serialize_cb_f cb, void *ctx); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LXB_CSS_VALUE_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/const.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/const.h new file mode 100644 index 0000000..a7bd64c --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/const.h @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_VALUE_CONST_H +#define LXB_CSS_VALUE_CONST_H + + +enum { + LXB_CSS_VALUE__UNDEF = 0x0000, + LXB_CSS_VALUE_INITIAL = 0x0001, + LXB_CSS_VALUE_INHERIT = 0x0002, + LXB_CSS_VALUE_UNSET = 0x0003, + LXB_CSS_VALUE_REVERT = 0x0004, + LXB_CSS_VALUE_FLEX_START = 0x0005, + LXB_CSS_VALUE_FLEX_END = 0x0006, + LXB_CSS_VALUE_CENTER = 0x0007, + LXB_CSS_VALUE_SPACE_BETWEEN = 0x0008, + LXB_CSS_VALUE_SPACE_AROUND = 0x0009, + LXB_CSS_VALUE_STRETCH = 0x000a, + LXB_CSS_VALUE_BASELINE = 0x000b, + LXB_CSS_VALUE_AUTO = 0x000c, + LXB_CSS_VALUE_TEXT_BOTTOM = 0x000d, + LXB_CSS_VALUE_ALPHABETIC = 0x000e, + LXB_CSS_VALUE_IDEOGRAPHIC = 0x000f, + LXB_CSS_VALUE_MIDDLE = 0x0010, + LXB_CSS_VALUE_CENTRAL = 0x0011, + LXB_CSS_VALUE_MATHEMATICAL = 0x0012, + LXB_CSS_VALUE_TEXT_TOP = 0x0013, + LXB_CSS_VALUE__LENGTH = 0x0014, + LXB_CSS_VALUE__PERCENTAGE = 0x0015, + LXB_CSS_VALUE_SUB = 0x0016, + LXB_CSS_VALUE_SUPER = 0x0017, + LXB_CSS_VALUE_TOP = 0x0018, + LXB_CSS_VALUE_BOTTOM = 0x0019, + LXB_CSS_VALUE_FIRST = 0x001a, + LXB_CSS_VALUE_LAST = 0x001b, + LXB_CSS_VALUE_THIN = 0x001c, + LXB_CSS_VALUE_MEDIUM = 0x001d, + LXB_CSS_VALUE_THICK = 0x001e, + LXB_CSS_VALUE_NONE = 0x001f, + LXB_CSS_VALUE_HIDDEN = 0x0020, + LXB_CSS_VALUE_DOTTED = 0x0021, + LXB_CSS_VALUE_DASHED = 0x0022, + LXB_CSS_VALUE_SOLID = 0x0023, + LXB_CSS_VALUE_DOUBLE = 0x0024, + LXB_CSS_VALUE_GROOVE = 0x0025, + LXB_CSS_VALUE_RIDGE = 0x0026, + LXB_CSS_VALUE_INSET = 0x0027, + LXB_CSS_VALUE_OUTSET = 0x0028, + LXB_CSS_VALUE_CONTENT_BOX = 0x0029, + LXB_CSS_VALUE_BORDER_BOX = 0x002a, + LXB_CSS_VALUE_INLINE_START = 0x002b, + LXB_CSS_VALUE_INLINE_END = 0x002c, + LXB_CSS_VALUE_BLOCK_START = 0x002d, + LXB_CSS_VALUE_BLOCK_END = 0x002e, + LXB_CSS_VALUE_LEFT = 0x002f, + LXB_CSS_VALUE_RIGHT = 0x0030, + LXB_CSS_VALUE_CURRENTCOLOR = 0x0031, + LXB_CSS_VALUE_TRANSPARENT = 0x0032, + LXB_CSS_VALUE_HEX = 0x0033, + LXB_CSS_VALUE_ALICEBLUE = 0x0034, + LXB_CSS_VALUE_ANTIQUEWHITE = 0x0035, + LXB_CSS_VALUE_AQUA = 0x0036, + LXB_CSS_VALUE_AQUAMARINE = 0x0037, + LXB_CSS_VALUE_AZURE = 0x0038, + LXB_CSS_VALUE_BEIGE = 0x0039, + LXB_CSS_VALUE_BISQUE = 0x003a, + LXB_CSS_VALUE_BLACK = 0x003b, + LXB_CSS_VALUE_BLANCHEDALMOND = 0x003c, + LXB_CSS_VALUE_BLUE = 0x003d, + LXB_CSS_VALUE_BLUEVIOLET = 0x003e, + LXB_CSS_VALUE_BROWN = 0x003f, + LXB_CSS_VALUE_BURLYWOOD = 0x0040, + LXB_CSS_VALUE_CADETBLUE = 0x0041, + LXB_CSS_VALUE_CHARTREUSE = 0x0042, + LXB_CSS_VALUE_CHOCOLATE = 0x0043, + LXB_CSS_VALUE_CORAL = 0x0044, + LXB_CSS_VALUE_CORNFLOWERBLUE = 0x0045, + LXB_CSS_VALUE_CORNSILK = 0x0046, + LXB_CSS_VALUE_CRIMSON = 0x0047, + LXB_CSS_VALUE_CYAN = 0x0048, + LXB_CSS_VALUE_DARKBLUE = 0x0049, + LXB_CSS_VALUE_DARKCYAN = 0x004a, + LXB_CSS_VALUE_DARKGOLDENROD = 0x004b, + LXB_CSS_VALUE_DARKGRAY = 0x004c, + LXB_CSS_VALUE_DARKGREEN = 0x004d, + LXB_CSS_VALUE_DARKGREY = 0x004e, + LXB_CSS_VALUE_DARKKHAKI = 0x004f, + LXB_CSS_VALUE_DARKMAGENTA = 0x0050, + LXB_CSS_VALUE_DARKOLIVEGREEN = 0x0051, + LXB_CSS_VALUE_DARKORANGE = 0x0052, + LXB_CSS_VALUE_DARKORCHID = 0x0053, + LXB_CSS_VALUE_DARKRED = 0x0054, + LXB_CSS_VALUE_DARKSALMON = 0x0055, + LXB_CSS_VALUE_DARKSEAGREEN = 0x0056, + LXB_CSS_VALUE_DARKSLATEBLUE = 0x0057, + LXB_CSS_VALUE_DARKSLATEGRAY = 0x0058, + LXB_CSS_VALUE_DARKSLATEGREY = 0x0059, + LXB_CSS_VALUE_DARKTURQUOISE = 0x005a, + LXB_CSS_VALUE_DARKVIOLET = 0x005b, + LXB_CSS_VALUE_DEEPPINK = 0x005c, + LXB_CSS_VALUE_DEEPSKYBLUE = 0x005d, + LXB_CSS_VALUE_DIMGRAY = 0x005e, + LXB_CSS_VALUE_DIMGREY = 0x005f, + LXB_CSS_VALUE_DODGERBLUE = 0x0060, + LXB_CSS_VALUE_FIREBRICK = 0x0061, + LXB_CSS_VALUE_FLORALWHITE = 0x0062, + LXB_CSS_VALUE_FORESTGREEN = 0x0063, + LXB_CSS_VALUE_FUCHSIA = 0x0064, + LXB_CSS_VALUE_GAINSBORO = 0x0065, + LXB_CSS_VALUE_GHOSTWHITE = 0x0066, + LXB_CSS_VALUE_GOLD = 0x0067, + LXB_CSS_VALUE_GOLDENROD = 0x0068, + LXB_CSS_VALUE_GRAY = 0x0069, + LXB_CSS_VALUE_GREEN = 0x006a, + LXB_CSS_VALUE_GREENYELLOW = 0x006b, + LXB_CSS_VALUE_GREY = 0x006c, + LXB_CSS_VALUE_HONEYDEW = 0x006d, + LXB_CSS_VALUE_HOTPINK = 0x006e, + LXB_CSS_VALUE_INDIANRED = 0x006f, + LXB_CSS_VALUE_INDIGO = 0x0070, + LXB_CSS_VALUE_IVORY = 0x0071, + LXB_CSS_VALUE_KHAKI = 0x0072, + LXB_CSS_VALUE_LAVENDER = 0x0073, + LXB_CSS_VALUE_LAVENDERBLUSH = 0x0074, + LXB_CSS_VALUE_LAWNGREEN = 0x0075, + LXB_CSS_VALUE_LEMONCHIFFON = 0x0076, + LXB_CSS_VALUE_LIGHTBLUE = 0x0077, + LXB_CSS_VALUE_LIGHTCORAL = 0x0078, + LXB_CSS_VALUE_LIGHTCYAN = 0x0079, + LXB_CSS_VALUE_LIGHTGOLDENRODYELLOW = 0x007a, + LXB_CSS_VALUE_LIGHTGRAY = 0x007b, + LXB_CSS_VALUE_LIGHTGREEN = 0x007c, + LXB_CSS_VALUE_LIGHTGREY = 0x007d, + LXB_CSS_VALUE_LIGHTPINK = 0x007e, + LXB_CSS_VALUE_LIGHTSALMON = 0x007f, + LXB_CSS_VALUE_LIGHTSEAGREEN = 0x0080, + LXB_CSS_VALUE_LIGHTSKYBLUE = 0x0081, + LXB_CSS_VALUE_LIGHTSLATEGRAY = 0x0082, + LXB_CSS_VALUE_LIGHTSLATEGREY = 0x0083, + LXB_CSS_VALUE_LIGHTSTEELBLUE = 0x0084, + LXB_CSS_VALUE_LIGHTYELLOW = 0x0085, + LXB_CSS_VALUE_LIME = 0x0086, + LXB_CSS_VALUE_LIMEGREEN = 0x0087, + LXB_CSS_VALUE_LINEN = 0x0088, + LXB_CSS_VALUE_MAGENTA = 0x0089, + LXB_CSS_VALUE_MAROON = 0x008a, + LXB_CSS_VALUE_MEDIUMAQUAMARINE = 0x008b, + LXB_CSS_VALUE_MEDIUMBLUE = 0x008c, + LXB_CSS_VALUE_MEDIUMORCHID = 0x008d, + LXB_CSS_VALUE_MEDIUMPURPLE = 0x008e, + LXB_CSS_VALUE_MEDIUMSEAGREEN = 0x008f, + LXB_CSS_VALUE_MEDIUMSLATEBLUE = 0x0090, + LXB_CSS_VALUE_MEDIUMSPRINGGREEN = 0x0091, + LXB_CSS_VALUE_MEDIUMTURQUOISE = 0x0092, + LXB_CSS_VALUE_MEDIUMVIOLETRED = 0x0093, + LXB_CSS_VALUE_MIDNIGHTBLUE = 0x0094, + LXB_CSS_VALUE_MINTCREAM = 0x0095, + LXB_CSS_VALUE_MISTYROSE = 0x0096, + LXB_CSS_VALUE_MOCCASIN = 0x0097, + LXB_CSS_VALUE_NAVAJOWHITE = 0x0098, + LXB_CSS_VALUE_NAVY = 0x0099, + LXB_CSS_VALUE_OLDLACE = 0x009a, + LXB_CSS_VALUE_OLIVE = 0x009b, + LXB_CSS_VALUE_OLIVEDRAB = 0x009c, + LXB_CSS_VALUE_ORANGE = 0x009d, + LXB_CSS_VALUE_ORANGERED = 0x009e, + LXB_CSS_VALUE_ORCHID = 0x009f, + LXB_CSS_VALUE_PALEGOLDENROD = 0x00a0, + LXB_CSS_VALUE_PALEGREEN = 0x00a1, + LXB_CSS_VALUE_PALETURQUOISE = 0x00a2, + LXB_CSS_VALUE_PALEVIOLETRED = 0x00a3, + LXB_CSS_VALUE_PAPAYAWHIP = 0x00a4, + LXB_CSS_VALUE_PEACHPUFF = 0x00a5, + LXB_CSS_VALUE_PERU = 0x00a6, + LXB_CSS_VALUE_PINK = 0x00a7, + LXB_CSS_VALUE_PLUM = 0x00a8, + LXB_CSS_VALUE_POWDERBLUE = 0x00a9, + LXB_CSS_VALUE_PURPLE = 0x00aa, + LXB_CSS_VALUE_REBECCAPURPLE = 0x00ab, + LXB_CSS_VALUE_RED = 0x00ac, + LXB_CSS_VALUE_ROSYBROWN = 0x00ad, + LXB_CSS_VALUE_ROYALBLUE = 0x00ae, + LXB_CSS_VALUE_SADDLEBROWN = 0x00af, + LXB_CSS_VALUE_SALMON = 0x00b0, + LXB_CSS_VALUE_SANDYBROWN = 0x00b1, + LXB_CSS_VALUE_SEAGREEN = 0x00b2, + LXB_CSS_VALUE_SEASHELL = 0x00b3, + LXB_CSS_VALUE_SIENNA = 0x00b4, + LXB_CSS_VALUE_SILVER = 0x00b5, + LXB_CSS_VALUE_SKYBLUE = 0x00b6, + LXB_CSS_VALUE_SLATEBLUE = 0x00b7, + LXB_CSS_VALUE_SLATEGRAY = 0x00b8, + LXB_CSS_VALUE_SLATEGREY = 0x00b9, + LXB_CSS_VALUE_SNOW = 0x00ba, + LXB_CSS_VALUE_SPRINGGREEN = 0x00bb, + LXB_CSS_VALUE_STEELBLUE = 0x00bc, + LXB_CSS_VALUE_TAN = 0x00bd, + LXB_CSS_VALUE_TEAL = 0x00be, + LXB_CSS_VALUE_THISTLE = 0x00bf, + LXB_CSS_VALUE_TOMATO = 0x00c0, + LXB_CSS_VALUE_TURQUOISE = 0x00c1, + LXB_CSS_VALUE_VIOLET = 0x00c2, + LXB_CSS_VALUE_WHEAT = 0x00c3, + LXB_CSS_VALUE_WHITE = 0x00c4, + LXB_CSS_VALUE_WHITESMOKE = 0x00c5, + LXB_CSS_VALUE_YELLOW = 0x00c6, + LXB_CSS_VALUE_YELLOWGREEN = 0x00c7, + LXB_CSS_VALUE_CANVAS = 0x00c8, + LXB_CSS_VALUE_CANVASTEXT = 0x00c9, + LXB_CSS_VALUE_LINKTEXT = 0x00ca, + LXB_CSS_VALUE_VISITEDTEXT = 0x00cb, + LXB_CSS_VALUE_ACTIVETEXT = 0x00cc, + LXB_CSS_VALUE_BUTTONFACE = 0x00cd, + LXB_CSS_VALUE_BUTTONTEXT = 0x00ce, + LXB_CSS_VALUE_BUTTONBORDER = 0x00cf, + LXB_CSS_VALUE_FIELD = 0x00d0, + LXB_CSS_VALUE_FIELDTEXT = 0x00d1, + LXB_CSS_VALUE_HIGHLIGHT = 0x00d2, + LXB_CSS_VALUE_HIGHLIGHTTEXT = 0x00d3, + LXB_CSS_VALUE_SELECTEDITEM = 0x00d4, + LXB_CSS_VALUE_SELECTEDITEMTEXT = 0x00d5, + LXB_CSS_VALUE_MARK = 0x00d6, + LXB_CSS_VALUE_MARKTEXT = 0x00d7, + LXB_CSS_VALUE_GRAYTEXT = 0x00d8, + LXB_CSS_VALUE_ACCENTCOLOR = 0x00d9, + LXB_CSS_VALUE_ACCENTCOLORTEXT = 0x00da, + LXB_CSS_VALUE_RGB = 0x00db, + LXB_CSS_VALUE_RGBA = 0x00dc, + LXB_CSS_VALUE_HSL = 0x00dd, + LXB_CSS_VALUE_HSLA = 0x00de, + LXB_CSS_VALUE_HWB = 0x00df, + LXB_CSS_VALUE_LAB = 0x00e0, + LXB_CSS_VALUE_LCH = 0x00e1, + LXB_CSS_VALUE_OKLAB = 0x00e2, + LXB_CSS_VALUE_OKLCH = 0x00e3, + LXB_CSS_VALUE_COLOR = 0x00e4, + LXB_CSS_VALUE_LTR = 0x00e5, + LXB_CSS_VALUE_RTL = 0x00e6, + LXB_CSS_VALUE_BLOCK = 0x00e7, + LXB_CSS_VALUE_INLINE = 0x00e8, + LXB_CSS_VALUE_RUN_IN = 0x00e9, + LXB_CSS_VALUE_FLOW = 0x00ea, + LXB_CSS_VALUE_FLOW_ROOT = 0x00eb, + LXB_CSS_VALUE_TABLE = 0x00ec, + LXB_CSS_VALUE_FLEX = 0x00ed, + LXB_CSS_VALUE_GRID = 0x00ee, + LXB_CSS_VALUE_RUBY = 0x00ef, + LXB_CSS_VALUE_LIST_ITEM = 0x00f0, + LXB_CSS_VALUE_TABLE_ROW_GROUP = 0x00f1, + LXB_CSS_VALUE_TABLE_HEADER_GROUP = 0x00f2, + LXB_CSS_VALUE_TABLE_FOOTER_GROUP = 0x00f3, + LXB_CSS_VALUE_TABLE_ROW = 0x00f4, + LXB_CSS_VALUE_TABLE_CELL = 0x00f5, + LXB_CSS_VALUE_TABLE_COLUMN_GROUP = 0x00f6, + LXB_CSS_VALUE_TABLE_COLUMN = 0x00f7, + LXB_CSS_VALUE_TABLE_CAPTION = 0x00f8, + LXB_CSS_VALUE_RUBY_BASE = 0x00f9, + LXB_CSS_VALUE_RUBY_TEXT = 0x00fa, + LXB_CSS_VALUE_RUBY_BASE_CONTAINER = 0x00fb, + LXB_CSS_VALUE_RUBY_TEXT_CONTAINER = 0x00fc, + LXB_CSS_VALUE_CONTENTS = 0x00fd, + LXB_CSS_VALUE_INLINE_BLOCK = 0x00fe, + LXB_CSS_VALUE_INLINE_TABLE = 0x00ff, + LXB_CSS_VALUE_INLINE_FLEX = 0x0100, + LXB_CSS_VALUE_INLINE_GRID = 0x0101, + LXB_CSS_VALUE_HANGING = 0x0102, + LXB_CSS_VALUE_CONTENT = 0x0103, + LXB_CSS_VALUE_ROW = 0x0104, + LXB_CSS_VALUE_ROW_REVERSE = 0x0105, + LXB_CSS_VALUE_COLUMN = 0x0106, + LXB_CSS_VALUE_COLUMN_REVERSE = 0x0107, + LXB_CSS_VALUE__NUMBER = 0x0108, + LXB_CSS_VALUE_NOWRAP = 0x0109, + LXB_CSS_VALUE_WRAP = 0x010a, + LXB_CSS_VALUE_WRAP_REVERSE = 0x010b, + LXB_CSS_VALUE_SNAP_BLOCK = 0x010c, + LXB_CSS_VALUE_START = 0x010d, + LXB_CSS_VALUE_END = 0x010e, + LXB_CSS_VALUE_NEAR = 0x010f, + LXB_CSS_VALUE_SNAP_INLINE = 0x0110, + LXB_CSS_VALUE__INTEGER = 0x0111, + LXB_CSS_VALUE_REGION = 0x0112, + LXB_CSS_VALUE_PAGE = 0x0113, + LXB_CSS_VALUE_SERIF = 0x0114, + LXB_CSS_VALUE_SANS_SERIF = 0x0115, + LXB_CSS_VALUE_CURSIVE = 0x0116, + LXB_CSS_VALUE_FANTASY = 0x0117, + LXB_CSS_VALUE_MONOSPACE = 0x0118, + LXB_CSS_VALUE_SYSTEM_UI = 0x0119, + LXB_CSS_VALUE_EMOJI = 0x011a, + LXB_CSS_VALUE_MATH = 0x011b, + LXB_CSS_VALUE_FANGSONG = 0x011c, + LXB_CSS_VALUE_UI_SERIF = 0x011d, + LXB_CSS_VALUE_UI_SANS_SERIF = 0x011e, + LXB_CSS_VALUE_UI_MONOSPACE = 0x011f, + LXB_CSS_VALUE_UI_ROUNDED = 0x0120, + LXB_CSS_VALUE_XX_SMALL = 0x0121, + LXB_CSS_VALUE_X_SMALL = 0x0122, + LXB_CSS_VALUE_SMALL = 0x0123, + LXB_CSS_VALUE_LARGE = 0x0124, + LXB_CSS_VALUE_X_LARGE = 0x0125, + LXB_CSS_VALUE_XX_LARGE = 0x0126, + LXB_CSS_VALUE_XXX_LARGE = 0x0127, + LXB_CSS_VALUE_LARGER = 0x0128, + LXB_CSS_VALUE_SMALLER = 0x0129, + LXB_CSS_VALUE_NORMAL = 0x012a, + LXB_CSS_VALUE_ULTRA_CONDENSED = 0x012b, + LXB_CSS_VALUE_EXTRA_CONDENSED = 0x012c, + LXB_CSS_VALUE_CONDENSED = 0x012d, + LXB_CSS_VALUE_SEMI_CONDENSED = 0x012e, + LXB_CSS_VALUE_SEMI_EXPANDED = 0x012f, + LXB_CSS_VALUE_EXPANDED = 0x0130, + LXB_CSS_VALUE_EXTRA_EXPANDED = 0x0131, + LXB_CSS_VALUE_ULTRA_EXPANDED = 0x0132, + LXB_CSS_VALUE_ITALIC = 0x0133, + LXB_CSS_VALUE_OBLIQUE = 0x0134, + LXB_CSS_VALUE_BOLD = 0x0135, + LXB_CSS_VALUE_BOLDER = 0x0136, + LXB_CSS_VALUE_LIGHTER = 0x0137, + LXB_CSS_VALUE_FORCE_END = 0x0138, + LXB_CSS_VALUE_ALLOW_END = 0x0139, + LXB_CSS_VALUE_MIN_CONTENT = 0x013a, + LXB_CSS_VALUE_MAX_CONTENT = 0x013b, + LXB_CSS_VALUE__ANGLE = 0x013c, + LXB_CSS_VALUE_MANUAL = 0x013d, + LXB_CSS_VALUE_LOOSE = 0x013e, + LXB_CSS_VALUE_STRICT = 0x013f, + LXB_CSS_VALUE_ANYWHERE = 0x0140, + LXB_CSS_VALUE_VISIBLE = 0x0141, + LXB_CSS_VALUE_CLIP = 0x0142, + LXB_CSS_VALUE_SCROLL = 0x0143, + LXB_CSS_VALUE_BREAK_WORD = 0x0144, + LXB_CSS_VALUE_STATIC = 0x0145, + LXB_CSS_VALUE_RELATIVE = 0x0146, + LXB_CSS_VALUE_ABSOLUTE = 0x0147, + LXB_CSS_VALUE_STICKY = 0x0148, + LXB_CSS_VALUE_FIXED = 0x0149, + LXB_CSS_VALUE_JUSTIFY = 0x014a, + LXB_CSS_VALUE_MATCH_PARENT = 0x014b, + LXB_CSS_VALUE_JUSTIFY_ALL = 0x014c, + LXB_CSS_VALUE_ALL = 0x014d, + LXB_CSS_VALUE_DIGITS = 0x014e, + LXB_CSS_VALUE_UNDERLINE = 0x014f, + LXB_CSS_VALUE_OVERLINE = 0x0150, + LXB_CSS_VALUE_LINE_THROUGH = 0x0151, + LXB_CSS_VALUE_BLINK = 0x0152, + LXB_CSS_VALUE_WAVY = 0x0153, + LXB_CSS_VALUE_EACH_LINE = 0x0154, + LXB_CSS_VALUE_INTER_WORD = 0x0155, + LXB_CSS_VALUE_INTER_CHARACTER = 0x0156, + LXB_CSS_VALUE_MIXED = 0x0157, + LXB_CSS_VALUE_UPRIGHT = 0x0158, + LXB_CSS_VALUE_SIDEWAYS = 0x0159, + LXB_CSS_VALUE_ELLIPSIS = 0x015a, + LXB_CSS_VALUE_CAPITALIZE = 0x015b, + LXB_CSS_VALUE_UPPERCASE = 0x015c, + LXB_CSS_VALUE_LOWERCASE = 0x015d, + LXB_CSS_VALUE_FULL_WIDTH = 0x015e, + LXB_CSS_VALUE_FULL_SIZE_KANA = 0x015f, + LXB_CSS_VALUE_EMBED = 0x0160, + LXB_CSS_VALUE_ISOLATE = 0x0161, + LXB_CSS_VALUE_BIDI_OVERRIDE = 0x0162, + LXB_CSS_VALUE_ISOLATE_OVERRIDE = 0x0163, + LXB_CSS_VALUE_PLAINTEXT = 0x0164, + LXB_CSS_VALUE_COLLAPSE = 0x0165, + LXB_CSS_VALUE_PRE = 0x0166, + LXB_CSS_VALUE_PRE_WRAP = 0x0167, + LXB_CSS_VALUE_BREAK_SPACES = 0x0168, + LXB_CSS_VALUE_PRE_LINE = 0x0169, + LXB_CSS_VALUE_KEEP_ALL = 0x016a, + LXB_CSS_VALUE_BREAK_ALL = 0x016b, + LXB_CSS_VALUE_BOTH = 0x016c, + LXB_CSS_VALUE_MINIMUM = 0x016d, + LXB_CSS_VALUE_MAXIMUM = 0x016e, + LXB_CSS_VALUE_CLEAR = 0x016f, + LXB_CSS_VALUE_HORIZONTAL_TB = 0x0170, + LXB_CSS_VALUE_VERTICAL_RL = 0x0171, + LXB_CSS_VALUE_VERTICAL_LR = 0x0172, + LXB_CSS_VALUE_SIDEWAYS_RL = 0x0173, + LXB_CSS_VALUE_SIDEWAYS_LR = 0x0174, + LXB_CSS_VALUE__LAST_ENTRY = 0x0175 +}; +typedef unsigned int lxb_css_value_type_t; + + +#endif /* LXB_CSS_VALUE_CONST_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/res.h b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/res.h new file mode 100644 index 0000000..180dc95 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/css/value/res.h @@ -0,0 +1,775 @@ +/* + * Copyright (C) 2026 Alexander Borisov + * + * Author: Alexander Borisov + */ + +/* + * Caution! + * This file generated by the script "utils/lexbor/css/names.py"! + * Do not change this file! + */ + + +#ifndef LXB_CSS_VALUE_RES_H +#define LXB_CSS_VALUE_RES_H + +#include "lexbor/core/shs.h" +#include "lexbor/css/value/const.h" + +static const lxb_css_data_t lxb_css_value_data[LXB_CSS_VALUE__LAST_ENTRY] = +{ + {(lxb_char_t *) "_undef", 6, LXB_CSS_VALUE__UNDEF}, + {(lxb_char_t *) "initial", 7, LXB_CSS_VALUE_INITIAL}, + {(lxb_char_t *) "inherit", 7, LXB_CSS_VALUE_INHERIT}, + {(lxb_char_t *) "unset", 5, LXB_CSS_VALUE_UNSET}, + {(lxb_char_t *) "revert", 6, LXB_CSS_VALUE_REVERT}, + {(lxb_char_t *) "flex-start", 10, LXB_CSS_VALUE_FLEX_START}, + {(lxb_char_t *) "flex-end", 8, LXB_CSS_VALUE_FLEX_END}, + {(lxb_char_t *) "center", 6, LXB_CSS_VALUE_CENTER}, + {(lxb_char_t *) "space-between", 13, LXB_CSS_VALUE_SPACE_BETWEEN}, + {(lxb_char_t *) "space-around", 12, LXB_CSS_VALUE_SPACE_AROUND}, + {(lxb_char_t *) "stretch", 7, LXB_CSS_VALUE_STRETCH}, + {(lxb_char_t *) "baseline", 8, LXB_CSS_VALUE_BASELINE}, + {(lxb_char_t *) "auto", 4, LXB_CSS_VALUE_AUTO}, + {(lxb_char_t *) "text-bottom", 11, LXB_CSS_VALUE_TEXT_BOTTOM}, + {(lxb_char_t *) "alphabetic", 10, LXB_CSS_VALUE_ALPHABETIC}, + {(lxb_char_t *) "ideographic", 11, LXB_CSS_VALUE_IDEOGRAPHIC}, + {(lxb_char_t *) "middle", 6, LXB_CSS_VALUE_MIDDLE}, + {(lxb_char_t *) "central", 7, LXB_CSS_VALUE_CENTRAL}, + {(lxb_char_t *) "mathematical", 12, LXB_CSS_VALUE_MATHEMATICAL}, + {(lxb_char_t *) "text-top", 8, LXB_CSS_VALUE_TEXT_TOP}, + {(lxb_char_t *) "_length", 7, LXB_CSS_VALUE__LENGTH}, + {(lxb_char_t *) "_percentage", 11, LXB_CSS_VALUE__PERCENTAGE}, + {(lxb_char_t *) "sub", 3, LXB_CSS_VALUE_SUB}, + {(lxb_char_t *) "super", 5, LXB_CSS_VALUE_SUPER}, + {(lxb_char_t *) "top", 3, LXB_CSS_VALUE_TOP}, + {(lxb_char_t *) "bottom", 6, LXB_CSS_VALUE_BOTTOM}, + {(lxb_char_t *) "first", 5, LXB_CSS_VALUE_FIRST}, + {(lxb_char_t *) "last", 4, LXB_CSS_VALUE_LAST}, + {(lxb_char_t *) "thin", 4, LXB_CSS_VALUE_THIN}, + {(lxb_char_t *) "medium", 6, LXB_CSS_VALUE_MEDIUM}, + {(lxb_char_t *) "thick", 5, LXB_CSS_VALUE_THICK}, + {(lxb_char_t *) "none", 4, LXB_CSS_VALUE_NONE}, + {(lxb_char_t *) "hidden", 6, LXB_CSS_VALUE_HIDDEN}, + {(lxb_char_t *) "dotted", 6, LXB_CSS_VALUE_DOTTED}, + {(lxb_char_t *) "dashed", 6, LXB_CSS_VALUE_DASHED}, + {(lxb_char_t *) "solid", 5, LXB_CSS_VALUE_SOLID}, + {(lxb_char_t *) "double", 6, LXB_CSS_VALUE_DOUBLE}, + {(lxb_char_t *) "groove", 6, LXB_CSS_VALUE_GROOVE}, + {(lxb_char_t *) "ridge", 5, LXB_CSS_VALUE_RIDGE}, + {(lxb_char_t *) "inset", 5, LXB_CSS_VALUE_INSET}, + {(lxb_char_t *) "outset", 6, LXB_CSS_VALUE_OUTSET}, + {(lxb_char_t *) "content-box", 11, LXB_CSS_VALUE_CONTENT_BOX}, + {(lxb_char_t *) "border-box", 10, LXB_CSS_VALUE_BORDER_BOX}, + {(lxb_char_t *) "inline-start", 12, LXB_CSS_VALUE_INLINE_START}, + {(lxb_char_t *) "inline-end", 10, LXB_CSS_VALUE_INLINE_END}, + {(lxb_char_t *) "block-start", 11, LXB_CSS_VALUE_BLOCK_START}, + {(lxb_char_t *) "block-end", 9, LXB_CSS_VALUE_BLOCK_END}, + {(lxb_char_t *) "left", 4, LXB_CSS_VALUE_LEFT}, + {(lxb_char_t *) "right", 5, LXB_CSS_VALUE_RIGHT}, + {(lxb_char_t *) "currentcolor", 12, LXB_CSS_VALUE_CURRENTCOLOR}, + {(lxb_char_t *) "transparent", 11, LXB_CSS_VALUE_TRANSPARENT}, + {(lxb_char_t *) "hex", 3, LXB_CSS_VALUE_HEX}, + {(lxb_char_t *) "aliceblue", 9, LXB_CSS_VALUE_ALICEBLUE}, + {(lxb_char_t *) "antiquewhite", 12, LXB_CSS_VALUE_ANTIQUEWHITE}, + {(lxb_char_t *) "aqua", 4, LXB_CSS_VALUE_AQUA}, + {(lxb_char_t *) "aquamarine", 10, LXB_CSS_VALUE_AQUAMARINE}, + {(lxb_char_t *) "azure", 5, LXB_CSS_VALUE_AZURE}, + {(lxb_char_t *) "beige", 5, LXB_CSS_VALUE_BEIGE}, + {(lxb_char_t *) "bisque", 6, LXB_CSS_VALUE_BISQUE}, + {(lxb_char_t *) "black", 5, LXB_CSS_VALUE_BLACK}, + {(lxb_char_t *) "blanchedalmond", 14, LXB_CSS_VALUE_BLANCHEDALMOND}, + {(lxb_char_t *) "blue", 4, LXB_CSS_VALUE_BLUE}, + {(lxb_char_t *) "blueviolet", 10, LXB_CSS_VALUE_BLUEVIOLET}, + {(lxb_char_t *) "brown", 5, LXB_CSS_VALUE_BROWN}, + {(lxb_char_t *) "burlywood", 9, LXB_CSS_VALUE_BURLYWOOD}, + {(lxb_char_t *) "cadetblue", 9, LXB_CSS_VALUE_CADETBLUE}, + {(lxb_char_t *) "chartreuse", 10, LXB_CSS_VALUE_CHARTREUSE}, + {(lxb_char_t *) "chocolate", 9, LXB_CSS_VALUE_CHOCOLATE}, + {(lxb_char_t *) "coral", 5, LXB_CSS_VALUE_CORAL}, + {(lxb_char_t *) "cornflowerblue", 14, LXB_CSS_VALUE_CORNFLOWERBLUE}, + {(lxb_char_t *) "cornsilk", 8, LXB_CSS_VALUE_CORNSILK}, + {(lxb_char_t *) "crimson", 7, LXB_CSS_VALUE_CRIMSON}, + {(lxb_char_t *) "cyan", 4, LXB_CSS_VALUE_CYAN}, + {(lxb_char_t *) "darkblue", 8, LXB_CSS_VALUE_DARKBLUE}, + {(lxb_char_t *) "darkcyan", 8, LXB_CSS_VALUE_DARKCYAN}, + {(lxb_char_t *) "darkgoldenrod", 13, LXB_CSS_VALUE_DARKGOLDENROD}, + {(lxb_char_t *) "darkgray", 8, LXB_CSS_VALUE_DARKGRAY}, + {(lxb_char_t *) "darkgreen", 9, LXB_CSS_VALUE_DARKGREEN}, + {(lxb_char_t *) "darkgrey", 8, LXB_CSS_VALUE_DARKGREY}, + {(lxb_char_t *) "darkkhaki", 9, LXB_CSS_VALUE_DARKKHAKI}, + {(lxb_char_t *) "darkmagenta", 11, LXB_CSS_VALUE_DARKMAGENTA}, + {(lxb_char_t *) "darkolivegreen", 14, LXB_CSS_VALUE_DARKOLIVEGREEN}, + {(lxb_char_t *) "darkorange", 10, LXB_CSS_VALUE_DARKORANGE}, + {(lxb_char_t *) "darkorchid", 10, LXB_CSS_VALUE_DARKORCHID}, + {(lxb_char_t *) "darkred", 7, LXB_CSS_VALUE_DARKRED}, + {(lxb_char_t *) "darksalmon", 10, LXB_CSS_VALUE_DARKSALMON}, + {(lxb_char_t *) "darkseagreen", 12, LXB_CSS_VALUE_DARKSEAGREEN}, + {(lxb_char_t *) "darkslateblue", 13, LXB_CSS_VALUE_DARKSLATEBLUE}, + {(lxb_char_t *) "darkslategray", 13, LXB_CSS_VALUE_DARKSLATEGRAY}, + {(lxb_char_t *) "darkslategrey", 13, LXB_CSS_VALUE_DARKSLATEGREY}, + {(lxb_char_t *) "darkturquoise", 13, LXB_CSS_VALUE_DARKTURQUOISE}, + {(lxb_char_t *) "darkviolet", 10, LXB_CSS_VALUE_DARKVIOLET}, + {(lxb_char_t *) "deeppink", 8, LXB_CSS_VALUE_DEEPPINK}, + {(lxb_char_t *) "deepskyblue", 11, LXB_CSS_VALUE_DEEPSKYBLUE}, + {(lxb_char_t *) "dimgray", 7, LXB_CSS_VALUE_DIMGRAY}, + {(lxb_char_t *) "dimgrey", 7, LXB_CSS_VALUE_DIMGREY}, + {(lxb_char_t *) "dodgerblue", 10, LXB_CSS_VALUE_DODGERBLUE}, + {(lxb_char_t *) "firebrick", 9, LXB_CSS_VALUE_FIREBRICK}, + {(lxb_char_t *) "floralwhite", 11, LXB_CSS_VALUE_FLORALWHITE}, + {(lxb_char_t *) "forestgreen", 11, LXB_CSS_VALUE_FORESTGREEN}, + {(lxb_char_t *) "fuchsia", 7, LXB_CSS_VALUE_FUCHSIA}, + {(lxb_char_t *) "gainsboro", 9, LXB_CSS_VALUE_GAINSBORO}, + {(lxb_char_t *) "ghostwhite", 10, LXB_CSS_VALUE_GHOSTWHITE}, + {(lxb_char_t *) "gold", 4, LXB_CSS_VALUE_GOLD}, + {(lxb_char_t *) "goldenrod", 9, LXB_CSS_VALUE_GOLDENROD}, + {(lxb_char_t *) "gray", 4, LXB_CSS_VALUE_GRAY}, + {(lxb_char_t *) "green", 5, LXB_CSS_VALUE_GREEN}, + {(lxb_char_t *) "greenyellow", 11, LXB_CSS_VALUE_GREENYELLOW}, + {(lxb_char_t *) "grey", 4, LXB_CSS_VALUE_GREY}, + {(lxb_char_t *) "honeydew", 8, LXB_CSS_VALUE_HONEYDEW}, + {(lxb_char_t *) "hotpink", 7, LXB_CSS_VALUE_HOTPINK}, + {(lxb_char_t *) "indianred", 9, LXB_CSS_VALUE_INDIANRED}, + {(lxb_char_t *) "indigo", 6, LXB_CSS_VALUE_INDIGO}, + {(lxb_char_t *) "ivory", 5, LXB_CSS_VALUE_IVORY}, + {(lxb_char_t *) "khaki", 5, LXB_CSS_VALUE_KHAKI}, + {(lxb_char_t *) "lavender", 8, LXB_CSS_VALUE_LAVENDER}, + {(lxb_char_t *) "lavenderblush", 13, LXB_CSS_VALUE_LAVENDERBLUSH}, + {(lxb_char_t *) "lawngreen", 9, LXB_CSS_VALUE_LAWNGREEN}, + {(lxb_char_t *) "lemonchiffon", 12, LXB_CSS_VALUE_LEMONCHIFFON}, + {(lxb_char_t *) "lightblue", 9, LXB_CSS_VALUE_LIGHTBLUE}, + {(lxb_char_t *) "lightcoral", 10, LXB_CSS_VALUE_LIGHTCORAL}, + {(lxb_char_t *) "lightcyan", 9, LXB_CSS_VALUE_LIGHTCYAN}, + {(lxb_char_t *) "lightgoldenrodyellow", 20, LXB_CSS_VALUE_LIGHTGOLDENRODYELLOW}, + {(lxb_char_t *) "lightgray", 9, LXB_CSS_VALUE_LIGHTGRAY}, + {(lxb_char_t *) "lightgreen", 10, LXB_CSS_VALUE_LIGHTGREEN}, + {(lxb_char_t *) "lightgrey", 9, LXB_CSS_VALUE_LIGHTGREY}, + {(lxb_char_t *) "lightpink", 9, LXB_CSS_VALUE_LIGHTPINK}, + {(lxb_char_t *) "lightsalmon", 11, LXB_CSS_VALUE_LIGHTSALMON}, + {(lxb_char_t *) "lightseagreen", 13, LXB_CSS_VALUE_LIGHTSEAGREEN}, + {(lxb_char_t *) "lightskyblue", 12, LXB_CSS_VALUE_LIGHTSKYBLUE}, + {(lxb_char_t *) "lightslategray", 14, LXB_CSS_VALUE_LIGHTSLATEGRAY}, + {(lxb_char_t *) "lightslategrey", 14, LXB_CSS_VALUE_LIGHTSLATEGREY}, + {(lxb_char_t *) "lightsteelblue", 14, LXB_CSS_VALUE_LIGHTSTEELBLUE}, + {(lxb_char_t *) "lightyellow", 11, LXB_CSS_VALUE_LIGHTYELLOW}, + {(lxb_char_t *) "lime", 4, LXB_CSS_VALUE_LIME}, + {(lxb_char_t *) "limegreen", 9, LXB_CSS_VALUE_LIMEGREEN}, + {(lxb_char_t *) "linen", 5, LXB_CSS_VALUE_LINEN}, + {(lxb_char_t *) "magenta", 7, LXB_CSS_VALUE_MAGENTA}, + {(lxb_char_t *) "maroon", 6, LXB_CSS_VALUE_MAROON}, + {(lxb_char_t *) "mediumaquamarine", 16, LXB_CSS_VALUE_MEDIUMAQUAMARINE}, + {(lxb_char_t *) "mediumblue", 10, LXB_CSS_VALUE_MEDIUMBLUE}, + {(lxb_char_t *) "mediumorchid", 12, LXB_CSS_VALUE_MEDIUMORCHID}, + {(lxb_char_t *) "mediumpurple", 12, LXB_CSS_VALUE_MEDIUMPURPLE}, + {(lxb_char_t *) "mediumseagreen", 14, LXB_CSS_VALUE_MEDIUMSEAGREEN}, + {(lxb_char_t *) "mediumslateblue", 15, LXB_CSS_VALUE_MEDIUMSLATEBLUE}, + {(lxb_char_t *) "mediumspringgreen", 17, LXB_CSS_VALUE_MEDIUMSPRINGGREEN}, + {(lxb_char_t *) "mediumturquoise", 15, LXB_CSS_VALUE_MEDIUMTURQUOISE}, + {(lxb_char_t *) "mediumvioletred", 15, LXB_CSS_VALUE_MEDIUMVIOLETRED}, + {(lxb_char_t *) "midnightblue", 12, LXB_CSS_VALUE_MIDNIGHTBLUE}, + {(lxb_char_t *) "mintcream", 9, LXB_CSS_VALUE_MINTCREAM}, + {(lxb_char_t *) "mistyrose", 9, LXB_CSS_VALUE_MISTYROSE}, + {(lxb_char_t *) "moccasin", 8, LXB_CSS_VALUE_MOCCASIN}, + {(lxb_char_t *) "navajowhite", 11, LXB_CSS_VALUE_NAVAJOWHITE}, + {(lxb_char_t *) "navy", 4, LXB_CSS_VALUE_NAVY}, + {(lxb_char_t *) "oldlace", 7, LXB_CSS_VALUE_OLDLACE}, + {(lxb_char_t *) "olive", 5, LXB_CSS_VALUE_OLIVE}, + {(lxb_char_t *) "olivedrab", 9, LXB_CSS_VALUE_OLIVEDRAB}, + {(lxb_char_t *) "orange", 6, LXB_CSS_VALUE_ORANGE}, + {(lxb_char_t *) "orangered", 9, LXB_CSS_VALUE_ORANGERED}, + {(lxb_char_t *) "orchid", 6, LXB_CSS_VALUE_ORCHID}, + {(lxb_char_t *) "palegoldenrod", 13, LXB_CSS_VALUE_PALEGOLDENROD}, + {(lxb_char_t *) "palegreen", 9, LXB_CSS_VALUE_PALEGREEN}, + {(lxb_char_t *) "paleturquoise", 13, LXB_CSS_VALUE_PALETURQUOISE}, + {(lxb_char_t *) "palevioletred", 13, LXB_CSS_VALUE_PALEVIOLETRED}, + {(lxb_char_t *) "papayawhip", 10, LXB_CSS_VALUE_PAPAYAWHIP}, + {(lxb_char_t *) "peachpuff", 9, LXB_CSS_VALUE_PEACHPUFF}, + {(lxb_char_t *) "peru", 4, LXB_CSS_VALUE_PERU}, + {(lxb_char_t *) "pink", 4, LXB_CSS_VALUE_PINK}, + {(lxb_char_t *) "plum", 4, LXB_CSS_VALUE_PLUM}, + {(lxb_char_t *) "powderblue", 10, LXB_CSS_VALUE_POWDERBLUE}, + {(lxb_char_t *) "purple", 6, LXB_CSS_VALUE_PURPLE}, + {(lxb_char_t *) "rebeccapurple", 13, LXB_CSS_VALUE_REBECCAPURPLE}, + {(lxb_char_t *) "red", 3, LXB_CSS_VALUE_RED}, + {(lxb_char_t *) "rosybrown", 9, LXB_CSS_VALUE_ROSYBROWN}, + {(lxb_char_t *) "royalblue", 9, LXB_CSS_VALUE_ROYALBLUE}, + {(lxb_char_t *) "saddlebrown", 11, LXB_CSS_VALUE_SADDLEBROWN}, + {(lxb_char_t *) "salmon", 6, LXB_CSS_VALUE_SALMON}, + {(lxb_char_t *) "sandybrown", 10, LXB_CSS_VALUE_SANDYBROWN}, + {(lxb_char_t *) "seagreen", 8, LXB_CSS_VALUE_SEAGREEN}, + {(lxb_char_t *) "seashell", 8, LXB_CSS_VALUE_SEASHELL}, + {(lxb_char_t *) "sienna", 6, LXB_CSS_VALUE_SIENNA}, + {(lxb_char_t *) "silver", 6, LXB_CSS_VALUE_SILVER}, + {(lxb_char_t *) "skyblue", 7, LXB_CSS_VALUE_SKYBLUE}, + {(lxb_char_t *) "slateblue", 9, LXB_CSS_VALUE_SLATEBLUE}, + {(lxb_char_t *) "slategray", 9, LXB_CSS_VALUE_SLATEGRAY}, + {(lxb_char_t *) "slategrey", 9, LXB_CSS_VALUE_SLATEGREY}, + {(lxb_char_t *) "snow", 4, LXB_CSS_VALUE_SNOW}, + {(lxb_char_t *) "springgreen", 11, LXB_CSS_VALUE_SPRINGGREEN}, + {(lxb_char_t *) "steelblue", 9, LXB_CSS_VALUE_STEELBLUE}, + {(lxb_char_t *) "tan", 3, LXB_CSS_VALUE_TAN}, + {(lxb_char_t *) "teal", 4, LXB_CSS_VALUE_TEAL}, + {(lxb_char_t *) "thistle", 7, LXB_CSS_VALUE_THISTLE}, + {(lxb_char_t *) "tomato", 6, LXB_CSS_VALUE_TOMATO}, + {(lxb_char_t *) "turquoise", 9, LXB_CSS_VALUE_TURQUOISE}, + {(lxb_char_t *) "violet", 6, LXB_CSS_VALUE_VIOLET}, + {(lxb_char_t *) "wheat", 5, LXB_CSS_VALUE_WHEAT}, + {(lxb_char_t *) "white", 5, LXB_CSS_VALUE_WHITE}, + {(lxb_char_t *) "whitesmoke", 10, LXB_CSS_VALUE_WHITESMOKE}, + {(lxb_char_t *) "yellow", 6, LXB_CSS_VALUE_YELLOW}, + {(lxb_char_t *) "yellowgreen", 11, LXB_CSS_VALUE_YELLOWGREEN}, + {(lxb_char_t *) "Canvas", 6, LXB_CSS_VALUE_CANVAS}, + {(lxb_char_t *) "CanvasText", 10, LXB_CSS_VALUE_CANVASTEXT}, + {(lxb_char_t *) "LinkText", 8, LXB_CSS_VALUE_LINKTEXT}, + {(lxb_char_t *) "VisitedText", 11, LXB_CSS_VALUE_VISITEDTEXT}, + {(lxb_char_t *) "ActiveText", 10, LXB_CSS_VALUE_ACTIVETEXT}, + {(lxb_char_t *) "ButtonFace", 10, LXB_CSS_VALUE_BUTTONFACE}, + {(lxb_char_t *) "ButtonText", 10, LXB_CSS_VALUE_BUTTONTEXT}, + {(lxb_char_t *) "ButtonBorder", 12, LXB_CSS_VALUE_BUTTONBORDER}, + {(lxb_char_t *) "Field", 5, LXB_CSS_VALUE_FIELD}, + {(lxb_char_t *) "FieldText", 9, LXB_CSS_VALUE_FIELDTEXT}, + {(lxb_char_t *) "Highlight", 9, LXB_CSS_VALUE_HIGHLIGHT}, + {(lxb_char_t *) "HighlightText", 13, LXB_CSS_VALUE_HIGHLIGHTTEXT}, + {(lxb_char_t *) "SelectedItem", 12, LXB_CSS_VALUE_SELECTEDITEM}, + {(lxb_char_t *) "SelectedItemText", 16, LXB_CSS_VALUE_SELECTEDITEMTEXT}, + {(lxb_char_t *) "Mark", 4, LXB_CSS_VALUE_MARK}, + {(lxb_char_t *) "MarkText", 8, LXB_CSS_VALUE_MARKTEXT}, + {(lxb_char_t *) "GrayText", 8, LXB_CSS_VALUE_GRAYTEXT}, + {(lxb_char_t *) "AccentColor", 11, LXB_CSS_VALUE_ACCENTCOLOR}, + {(lxb_char_t *) "AccentColorText", 15, LXB_CSS_VALUE_ACCENTCOLORTEXT}, + {(lxb_char_t *) "rgb", 3, LXB_CSS_VALUE_RGB}, + {(lxb_char_t *) "rgba", 4, LXB_CSS_VALUE_RGBA}, + {(lxb_char_t *) "hsl", 3, LXB_CSS_VALUE_HSL}, + {(lxb_char_t *) "hsla", 4, LXB_CSS_VALUE_HSLA}, + {(lxb_char_t *) "hwb", 3, LXB_CSS_VALUE_HWB}, + {(lxb_char_t *) "lab", 3, LXB_CSS_VALUE_LAB}, + {(lxb_char_t *) "lch", 3, LXB_CSS_VALUE_LCH}, + {(lxb_char_t *) "oklab", 5, LXB_CSS_VALUE_OKLAB}, + {(lxb_char_t *) "oklch", 5, LXB_CSS_VALUE_OKLCH}, + {(lxb_char_t *) "color", 5, LXB_CSS_VALUE_COLOR}, + {(lxb_char_t *) "ltr", 3, LXB_CSS_VALUE_LTR}, + {(lxb_char_t *) "rtl", 3, LXB_CSS_VALUE_RTL}, + {(lxb_char_t *) "block", 5, LXB_CSS_VALUE_BLOCK}, + {(lxb_char_t *) "inline", 6, LXB_CSS_VALUE_INLINE}, + {(lxb_char_t *) "run-in", 6, LXB_CSS_VALUE_RUN_IN}, + {(lxb_char_t *) "flow", 4, LXB_CSS_VALUE_FLOW}, + {(lxb_char_t *) "flow-root", 9, LXB_CSS_VALUE_FLOW_ROOT}, + {(lxb_char_t *) "table", 5, LXB_CSS_VALUE_TABLE}, + {(lxb_char_t *) "flex", 4, LXB_CSS_VALUE_FLEX}, + {(lxb_char_t *) "grid", 4, LXB_CSS_VALUE_GRID}, + {(lxb_char_t *) "ruby", 4, LXB_CSS_VALUE_RUBY}, + {(lxb_char_t *) "list-item", 9, LXB_CSS_VALUE_LIST_ITEM}, + {(lxb_char_t *) "table-row-group", 15, LXB_CSS_VALUE_TABLE_ROW_GROUP}, + {(lxb_char_t *) "table-header-group", 18, LXB_CSS_VALUE_TABLE_HEADER_GROUP}, + {(lxb_char_t *) "table-footer-group", 18, LXB_CSS_VALUE_TABLE_FOOTER_GROUP}, + {(lxb_char_t *) "table-row", 9, LXB_CSS_VALUE_TABLE_ROW}, + {(lxb_char_t *) "table-cell", 10, LXB_CSS_VALUE_TABLE_CELL}, + {(lxb_char_t *) "table-column-group", 18, LXB_CSS_VALUE_TABLE_COLUMN_GROUP}, + {(lxb_char_t *) "table-column", 12, LXB_CSS_VALUE_TABLE_COLUMN}, + {(lxb_char_t *) "table-caption", 13, LXB_CSS_VALUE_TABLE_CAPTION}, + {(lxb_char_t *) "ruby-base", 9, LXB_CSS_VALUE_RUBY_BASE}, + {(lxb_char_t *) "ruby-text", 9, LXB_CSS_VALUE_RUBY_TEXT}, + {(lxb_char_t *) "ruby-base-container", 19, LXB_CSS_VALUE_RUBY_BASE_CONTAINER}, + {(lxb_char_t *) "ruby-text-container", 19, LXB_CSS_VALUE_RUBY_TEXT_CONTAINER}, + {(lxb_char_t *) "contents", 8, LXB_CSS_VALUE_CONTENTS}, + {(lxb_char_t *) "inline-block", 12, LXB_CSS_VALUE_INLINE_BLOCK}, + {(lxb_char_t *) "inline-table", 12, LXB_CSS_VALUE_INLINE_TABLE}, + {(lxb_char_t *) "inline-flex", 11, LXB_CSS_VALUE_INLINE_FLEX}, + {(lxb_char_t *) "inline-grid", 11, LXB_CSS_VALUE_INLINE_GRID}, + {(lxb_char_t *) "hanging", 7, LXB_CSS_VALUE_HANGING}, + {(lxb_char_t *) "content", 7, LXB_CSS_VALUE_CONTENT}, + {(lxb_char_t *) "row", 3, LXB_CSS_VALUE_ROW}, + {(lxb_char_t *) "row-reverse", 11, LXB_CSS_VALUE_ROW_REVERSE}, + {(lxb_char_t *) "column", 6, LXB_CSS_VALUE_COLUMN}, + {(lxb_char_t *) "column-reverse", 14, LXB_CSS_VALUE_COLUMN_REVERSE}, + {(lxb_char_t *) "_number", 7, LXB_CSS_VALUE__NUMBER}, + {(lxb_char_t *) "nowrap", 6, LXB_CSS_VALUE_NOWRAP}, + {(lxb_char_t *) "wrap", 4, LXB_CSS_VALUE_WRAP}, + {(lxb_char_t *) "wrap-reverse", 12, LXB_CSS_VALUE_WRAP_REVERSE}, + {(lxb_char_t *) "snap-block", 10, LXB_CSS_VALUE_SNAP_BLOCK}, + {(lxb_char_t *) "start", 5, LXB_CSS_VALUE_START}, + {(lxb_char_t *) "end", 3, LXB_CSS_VALUE_END}, + {(lxb_char_t *) "near", 4, LXB_CSS_VALUE_NEAR}, + {(lxb_char_t *) "snap-inline", 11, LXB_CSS_VALUE_SNAP_INLINE}, + {(lxb_char_t *) "_integer", 8, LXB_CSS_VALUE__INTEGER}, + {(lxb_char_t *) "region", 6, LXB_CSS_VALUE_REGION}, + {(lxb_char_t *) "page", 4, LXB_CSS_VALUE_PAGE}, + {(lxb_char_t *) "serif", 5, LXB_CSS_VALUE_SERIF}, + {(lxb_char_t *) "sans-serif", 10, LXB_CSS_VALUE_SANS_SERIF}, + {(lxb_char_t *) "cursive", 7, LXB_CSS_VALUE_CURSIVE}, + {(lxb_char_t *) "fantasy", 7, LXB_CSS_VALUE_FANTASY}, + {(lxb_char_t *) "monospace", 9, LXB_CSS_VALUE_MONOSPACE}, + {(lxb_char_t *) "system-ui", 9, LXB_CSS_VALUE_SYSTEM_UI}, + {(lxb_char_t *) "emoji", 5, LXB_CSS_VALUE_EMOJI}, + {(lxb_char_t *) "math", 4, LXB_CSS_VALUE_MATH}, + {(lxb_char_t *) "fangsong", 8, LXB_CSS_VALUE_FANGSONG}, + {(lxb_char_t *) "ui-serif", 8, LXB_CSS_VALUE_UI_SERIF}, + {(lxb_char_t *) "ui-sans-serif", 13, LXB_CSS_VALUE_UI_SANS_SERIF}, + {(lxb_char_t *) "ui-monospace", 12, LXB_CSS_VALUE_UI_MONOSPACE}, + {(lxb_char_t *) "ui-rounded", 10, LXB_CSS_VALUE_UI_ROUNDED}, + {(lxb_char_t *) "xx-small", 8, LXB_CSS_VALUE_XX_SMALL}, + {(lxb_char_t *) "x-small", 7, LXB_CSS_VALUE_X_SMALL}, + {(lxb_char_t *) "small", 5, LXB_CSS_VALUE_SMALL}, + {(lxb_char_t *) "large", 5, LXB_CSS_VALUE_LARGE}, + {(lxb_char_t *) "x-large", 7, LXB_CSS_VALUE_X_LARGE}, + {(lxb_char_t *) "xx-large", 8, LXB_CSS_VALUE_XX_LARGE}, + {(lxb_char_t *) "xxx-large", 9, LXB_CSS_VALUE_XXX_LARGE}, + {(lxb_char_t *) "larger", 6, LXB_CSS_VALUE_LARGER}, + {(lxb_char_t *) "smaller", 7, LXB_CSS_VALUE_SMALLER}, + {(lxb_char_t *) "normal", 6, LXB_CSS_VALUE_NORMAL}, + {(lxb_char_t *) "ultra-condensed", 15, LXB_CSS_VALUE_ULTRA_CONDENSED}, + {(lxb_char_t *) "extra-condensed", 15, LXB_CSS_VALUE_EXTRA_CONDENSED}, + {(lxb_char_t *) "condensed", 9, LXB_CSS_VALUE_CONDENSED}, + {(lxb_char_t *) "semi-condensed", 14, LXB_CSS_VALUE_SEMI_CONDENSED}, + {(lxb_char_t *) "semi-expanded", 13, LXB_CSS_VALUE_SEMI_EXPANDED}, + {(lxb_char_t *) "expanded", 8, LXB_CSS_VALUE_EXPANDED}, + {(lxb_char_t *) "extra-expanded", 14, LXB_CSS_VALUE_EXTRA_EXPANDED}, + {(lxb_char_t *) "ultra-expanded", 14, LXB_CSS_VALUE_ULTRA_EXPANDED}, + {(lxb_char_t *) "italic", 6, LXB_CSS_VALUE_ITALIC}, + {(lxb_char_t *) "oblique", 7, LXB_CSS_VALUE_OBLIQUE}, + {(lxb_char_t *) "bold", 4, LXB_CSS_VALUE_BOLD}, + {(lxb_char_t *) "bolder", 6, LXB_CSS_VALUE_BOLDER}, + {(lxb_char_t *) "lighter", 7, LXB_CSS_VALUE_LIGHTER}, + {(lxb_char_t *) "force-end", 9, LXB_CSS_VALUE_FORCE_END}, + {(lxb_char_t *) "allow-end", 9, LXB_CSS_VALUE_ALLOW_END}, + {(lxb_char_t *) "min-content", 11, LXB_CSS_VALUE_MIN_CONTENT}, + {(lxb_char_t *) "max-content", 11, LXB_CSS_VALUE_MAX_CONTENT}, + {(lxb_char_t *) "_angle", 6, LXB_CSS_VALUE__ANGLE}, + {(lxb_char_t *) "manual", 6, LXB_CSS_VALUE_MANUAL}, + {(lxb_char_t *) "loose", 5, LXB_CSS_VALUE_LOOSE}, + {(lxb_char_t *) "strict", 6, LXB_CSS_VALUE_STRICT}, + {(lxb_char_t *) "anywhere", 8, LXB_CSS_VALUE_ANYWHERE}, + {(lxb_char_t *) "visible", 7, LXB_CSS_VALUE_VISIBLE}, + {(lxb_char_t *) "clip", 4, LXB_CSS_VALUE_CLIP}, + {(lxb_char_t *) "scroll", 6, LXB_CSS_VALUE_SCROLL}, + {(lxb_char_t *) "break-word", 10, LXB_CSS_VALUE_BREAK_WORD}, + {(lxb_char_t *) "static", 6, LXB_CSS_VALUE_STATIC}, + {(lxb_char_t *) "relative", 8, LXB_CSS_VALUE_RELATIVE}, + {(lxb_char_t *) "absolute", 8, LXB_CSS_VALUE_ABSOLUTE}, + {(lxb_char_t *) "sticky", 6, LXB_CSS_VALUE_STICKY}, + {(lxb_char_t *) "fixed", 5, LXB_CSS_VALUE_FIXED}, + {(lxb_char_t *) "justify", 7, LXB_CSS_VALUE_JUSTIFY}, + {(lxb_char_t *) "match-parent", 12, LXB_CSS_VALUE_MATCH_PARENT}, + {(lxb_char_t *) "justify-all", 11, LXB_CSS_VALUE_JUSTIFY_ALL}, + {(lxb_char_t *) "all", 3, LXB_CSS_VALUE_ALL}, + {(lxb_char_t *) "digits", 6, LXB_CSS_VALUE_DIGITS}, + {(lxb_char_t *) "underline", 9, LXB_CSS_VALUE_UNDERLINE}, + {(lxb_char_t *) "overline", 8, LXB_CSS_VALUE_OVERLINE}, + {(lxb_char_t *) "line-through", 12, LXB_CSS_VALUE_LINE_THROUGH}, + {(lxb_char_t *) "blink", 5, LXB_CSS_VALUE_BLINK}, + {(lxb_char_t *) "wavy", 4, LXB_CSS_VALUE_WAVY}, + {(lxb_char_t *) "each-line", 9, LXB_CSS_VALUE_EACH_LINE}, + {(lxb_char_t *) "inter-word", 10, LXB_CSS_VALUE_INTER_WORD}, + {(lxb_char_t *) "inter-character", 15, LXB_CSS_VALUE_INTER_CHARACTER}, + {(lxb_char_t *) "mixed", 5, LXB_CSS_VALUE_MIXED}, + {(lxb_char_t *) "upright", 7, LXB_CSS_VALUE_UPRIGHT}, + {(lxb_char_t *) "sideways", 8, LXB_CSS_VALUE_SIDEWAYS}, + {(lxb_char_t *) "ellipsis", 8, LXB_CSS_VALUE_ELLIPSIS}, + {(lxb_char_t *) "capitalize", 10, LXB_CSS_VALUE_CAPITALIZE}, + {(lxb_char_t *) "uppercase", 9, LXB_CSS_VALUE_UPPERCASE}, + {(lxb_char_t *) "lowercase", 9, LXB_CSS_VALUE_LOWERCASE}, + {(lxb_char_t *) "full-width", 10, LXB_CSS_VALUE_FULL_WIDTH}, + {(lxb_char_t *) "full-size-kana", 14, LXB_CSS_VALUE_FULL_SIZE_KANA}, + {(lxb_char_t *) "embed", 5, LXB_CSS_VALUE_EMBED}, + {(lxb_char_t *) "isolate", 7, LXB_CSS_VALUE_ISOLATE}, + {(lxb_char_t *) "bidi-override", 13, LXB_CSS_VALUE_BIDI_OVERRIDE}, + {(lxb_char_t *) "isolate-override", 16, LXB_CSS_VALUE_ISOLATE_OVERRIDE}, + {(lxb_char_t *) "plaintext", 9, LXB_CSS_VALUE_PLAINTEXT}, + {(lxb_char_t *) "collapse", 8, LXB_CSS_VALUE_COLLAPSE}, + {(lxb_char_t *) "pre", 3, LXB_CSS_VALUE_PRE}, + {(lxb_char_t *) "pre-wrap", 8, LXB_CSS_VALUE_PRE_WRAP}, + {(lxb_char_t *) "break-spaces", 12, LXB_CSS_VALUE_BREAK_SPACES}, + {(lxb_char_t *) "pre-line", 8, LXB_CSS_VALUE_PRE_LINE}, + {(lxb_char_t *) "keep-all", 8, LXB_CSS_VALUE_KEEP_ALL}, + {(lxb_char_t *) "break-all", 9, LXB_CSS_VALUE_BREAK_ALL}, + {(lxb_char_t *) "both", 4, LXB_CSS_VALUE_BOTH}, + {(lxb_char_t *) "minimum", 7, LXB_CSS_VALUE_MINIMUM}, + {(lxb_char_t *) "maximum", 7, LXB_CSS_VALUE_MAXIMUM}, + {(lxb_char_t *) "clear", 5, LXB_CSS_VALUE_CLEAR}, + {(lxb_char_t *) "horizontal-tb", 13, LXB_CSS_VALUE_HORIZONTAL_TB}, + {(lxb_char_t *) "vertical-rl", 11, LXB_CSS_VALUE_VERTICAL_RL}, + {(lxb_char_t *) "vertical-lr", 11, LXB_CSS_VALUE_VERTICAL_LR}, + {(lxb_char_t *) "sideways-rl", 11, LXB_CSS_VALUE_SIDEWAYS_RL}, + {(lxb_char_t *) "sideways-lr", 11, LXB_CSS_VALUE_SIDEWAYS_LR} +}; + +static const lexbor_shs_entry_t lxb_css_value_shs[373] = +{ + {NULL, NULL, 253, 0}, + {"salmon", (void *) LXB_CSS_VALUE_SALMON, 6, 2}, + {"canvas", (void *) LXB_CSS_VALUE_CANVAS, 6, 4}, + {"allow-end", (void *) LXB_CSS_VALUE_ALLOW_END, 9, 0}, + {"static", (void *) LXB_CSS_VALUE_STATIC, 6, 5}, + {"sticky", (void *) LXB_CSS_VALUE_STICKY, 6, 0}, + {"darkviolet", (void *) LXB_CSS_VALUE_DARKVIOLET, 10, 0}, + {"blue", (void *) LXB_CSS_VALUE_BLUE, 4, 1}, + {"end", (void *) LXB_CSS_VALUE_END, 3, 24}, + {"middle", (void *) LXB_CSS_VALUE_MIDDLE, 6, 32}, + {"rgb", (void *) LXB_CSS_VALUE_RGB, 3, 59}, + {"baseline", (void *) LXB_CSS_VALUE_BASELINE, 8, 69}, + {"mistyrose", (void *) LXB_CSS_VALUE_MISTYROSE, 9, 71}, + {"expanded", (void *) LXB_CSS_VALUE_EXPANDED, 8, 92}, + {"space-between", (void *) LXB_CSS_VALUE_SPACE_BETWEEN, 13, 0}, + {"mediumpurple", (void *) LXB_CSS_VALUE_MEDIUMPURPLE, 12, 99}, + {"bidi-override", (void *) LXB_CSS_VALUE_BIDI_OVERRIDE, 13, 0}, + {"bold", (void *) LXB_CSS_VALUE_BOLD, 4, 103}, + {"mixed", (void *) LXB_CSS_VALUE_MIXED, 5, 105}, + {"extra-expanded", (void *) LXB_CSS_VALUE_EXTRA_EXPANDED, 14, 119}, + {"auto", (void *) LXB_CSS_VALUE_AUTO, 4, 129}, + {"teal", (void *) LXB_CSS_VALUE_TEAL, 4, 130}, + {"lab", (void *) LXB_CSS_VALUE_LAB, 3, 131}, + {"break-word", (void *) LXB_CSS_VALUE_BREAK_WORD, 10, 0}, + {"beige", (void *) LXB_CSS_VALUE_BEIGE, 5, 0}, + {"mediumorchid", (void *) LXB_CSS_VALUE_MEDIUMORCHID, 12, 0}, + {"vertical-lr", (void *) LXB_CSS_VALUE_VERTICAL_LR, 11, 0}, + {"table-cell", (void *) LXB_CSS_VALUE_TABLE_CELL, 10, 135}, + {"pre-wrap", (void *) LXB_CSS_VALUE_PRE_WRAP, 8, 136}, + {"super", (void *) LXB_CSS_VALUE_SUPER, 5, 0}, + {"silver", (void *) LXB_CSS_VALUE_SILVER, 6, 137}, + {"hsl", (void *) LXB_CSS_VALUE_HSL, 3, 140}, + {"bisque", (void *) LXB_CSS_VALUE_BISQUE, 6, 53}, + {"darkblue", (void *) LXB_CSS_VALUE_DARKBLUE, 8, 0}, + {"hex", (void *) LXB_CSS_VALUE_HEX, 3, 0}, + {"darkorange", (void *) LXB_CSS_VALUE_DARKORANGE, 10, 143}, + {"lightcoral", (void *) LXB_CSS_VALUE_LIGHTCORAL, 10, 145}, + {"table-row", (void *) LXB_CSS_VALUE_TABLE_ROW, 9, 0}, + {"ridge", (void *) LXB_CSS_VALUE_RIDGE, 5, 149}, + {"violet", (void *) LXB_CSS_VALUE_VIOLET, 6, 156}, + {"italic", (void *) LXB_CSS_VALUE_ITALIC, 6, 157}, + {"relative", (void *) LXB_CSS_VALUE_RELATIVE, 8, 0}, + {"royalblue", (void *) LXB_CSS_VALUE_ROYALBLUE, 9, 179}, + {"highlighttext", (void *) LXB_CSS_VALUE_HIGHLIGHTTEXT, 13, 0}, + {"visitedtext", (void *) LXB_CSS_VALUE_VISITEDTEXT, 11, 193}, + {"ideographic", (void *) LXB_CSS_VALUE_IDEOGRAPHIC, 11, 0}, + {"outset", (void *) LXB_CSS_VALUE_OUTSET, 6, 194}, + {"azure", (void *) LXB_CSS_VALUE_AZURE, 5, 0}, + {"pink", (void *) LXB_CSS_VALUE_PINK, 4, 0}, + {"near", (void *) LXB_CSS_VALUE_NEAR, 4, 0}, + {"magenta", (void *) LXB_CSS_VALUE_MAGENTA, 7, 204}, + {"aliceblue", (void *) LXB_CSS_VALUE_ALICEBLUE, 9, 0}, + {"aquamarine", (void *) LXB_CSS_VALUE_AQUAMARINE, 10, 0}, + {"seagreen", (void *) LXB_CSS_VALUE_SEAGREEN, 8, 55}, + {"antiquewhite", (void *) LXB_CSS_VALUE_ANTIQUEWHITE, 12, 0}, + {"contents", (void *) LXB_CSS_VALUE_CONTENTS, 8, 56}, + {"lightpink", (void *) LXB_CSS_VALUE_LIGHTPINK, 9, 0}, + {"snap-block", (void *) LXB_CSS_VALUE_SNAP_BLOCK, 10, 0}, + {"white", (void *) LXB_CSS_VALUE_WHITE, 5, 0}, + {"embed", (void *) LXB_CSS_VALUE_EMBED, 5, 66}, + {"first", (void *) LXB_CSS_VALUE_FIRST, 5, 211}, + {"groove", (void *) LXB_CSS_VALUE_GROOVE, 6, 212}, + {"normal", (void *) LXB_CSS_VALUE_NORMAL, 6, 0}, + {"whitesmoke", (void *) LXB_CSS_VALUE_WHITESMOKE, 10, 0}, + {"fieldtext", (void *) LXB_CSS_VALUE_FIELDTEXT, 9, 221}, + {"flex-start", (void *) LXB_CSS_VALUE_FLEX_START, 10, 227}, + {"slategray", (void *) LXB_CSS_VALUE_SLATEGRAY, 9, 67}, + {"slategrey", (void *) LXB_CSS_VALUE_SLATEGREY, 9, 68}, + {"darkmagenta", (void *) LXB_CSS_VALUE_DARKMAGENTA, 11, 0}, + {"sandybrown", (void *) LXB_CSS_VALUE_SANDYBROWN, 10, 0}, + {"field", (void *) LXB_CSS_VALUE_FIELD, 5, 234}, + {"monospace", (void *) LXB_CSS_VALUE_MONOSPACE, 9, 78}, + {"color", (void *) LXB_CSS_VALUE_COLOR, 5, 236}, + {"center", (void *) LXB_CSS_VALUE_CENTER, 6, 240}, + {"force-end", (void *) LXB_CSS_VALUE_FORCE_END, 9, 0}, + {"solid", (void *) LXB_CSS_VALUE_SOLID, 5, 243}, + {"gold", (void *) LXB_CSS_VALUE_GOLD, 4, 248}, + {"peachpuff", (void *) LXB_CSS_VALUE_PEACHPUFF, 9, 0}, + {"saddlebrown", (void *) LXB_CSS_VALUE_SADDLEBROWN, 11, 86}, + {"inline", (void *) LXB_CSS_VALUE_INLINE, 6, 254}, + {"isolate", (void *) LXB_CSS_VALUE_ISOLATE, 7, 0}, + {"goldenrod", (void *) LXB_CSS_VALUE_GOLDENROD, 9, 0}, + {"cyan", (void *) LXB_CSS_VALUE_CYAN, 4, 256}, + {"linen", (void *) LXB_CSS_VALUE_LINEN, 5, 257}, + {"column", (void *) LXB_CSS_VALUE_COLUMN, 6, 258}, + {"crimson", (void *) LXB_CSS_VALUE_CRIMSON, 7, 259}, + {"springgreen", (void *) LXB_CSS_VALUE_SPRINGGREEN, 11, 0}, + {"lawngreen", (void *) LXB_CSS_VALUE_LAWNGREEN, 9, 260}, + {"lightgreen", (void *) LXB_CSS_VALUE_LIGHTGREEN, 10, 0}, + {"plum", (void *) LXB_CSS_VALUE_PLUM, 4, 262}, + {"initial", (void *) LXB_CSS_VALUE_INITIAL, 7, 264}, + {"lightseagreen", (void *) LXB_CSS_VALUE_LIGHTSEAGREEN, 13, 0}, + {"mediumblue", (void *) LXB_CSS_VALUE_MEDIUMBLUE, 10, 93}, + {"activetext", (void *) LXB_CSS_VALUE_ACTIVETEXT, 10, 94}, + {"buttonface", (void *) LXB_CSS_VALUE_BUTTONFACE, 10, 96}, + {"each-line", (void *) LXB_CSS_VALUE_EACH_LINE, 9, 0}, + {"greenyellow", (void *) LXB_CSS_VALUE_GREENYELLOW, 11, 0}, + {"hanging", (void *) LXB_CSS_VALUE_HANGING, 7, 0}, + {"hotpink", (void *) LXB_CSS_VALUE_HOTPINK, 7, 0}, + {"midnightblue", (void *) LXB_CSS_VALUE_MIDNIGHTBLUE, 12, 0}, + {"stretch", (void *) LXB_CSS_VALUE_STRETCH, 7, 0}, + {"lime", (void *) LXB_CSS_VALUE_LIME, 4, 266}, + {"unset", (void *) LXB_CSS_VALUE_UNSET, 5, 267}, + {"x-small", (void *) LXB_CSS_VALUE_X_SMALL, 7, 0}, + {"upright", (void *) LXB_CSS_VALUE_UPRIGHT, 7, 0}, + {"xx-small", (void *) LXB_CSS_VALUE_XX_SMALL, 8, 108}, + {"lightblue", (void *) LXB_CSS_VALUE_LIGHTBLUE, 9, 270}, + {"aqua", (void *) LXB_CSS_VALUE_AQUA, 4, 271}, + {"mediumslateblue", (void *) LXB_CSS_VALUE_MEDIUMSLATEBLUE, 15, 110}, + {"lightskyblue", (void *) LXB_CSS_VALUE_LIGHTSKYBLUE, 12, 0}, + {"mediumturquoise", (void *) LXB_CSS_VALUE_MEDIUMTURQUOISE, 15, 113}, + {"plaintext", (void *) LXB_CSS_VALUE_PLAINTEXT, 9, 272}, + {"justify-all", (void *) LXB_CSS_VALUE_JUSTIFY_ALL, 11, 0}, + {"accentcolortext", (void *) LXB_CSS_VALUE_ACCENTCOLORTEXT, 15, 0}, + {"tan", (void *) LXB_CSS_VALUE_TAN, 3, 273}, + {"thin", (void *) LXB_CSS_VALUE_THIN, 4, 274}, + {"blueviolet", (void *) LXB_CSS_VALUE_BLUEVIOLET, 10, 275}, + {"run-in", (void *) LXB_CSS_VALUE_RUN_IN, 6, 276}, + {"match-parent", (void *) LXB_CSS_VALUE_MATCH_PARENT, 12, 0}, + {"mediumaquamarine", (void *) LXB_CSS_VALUE_MEDIUMAQUAMARINE, 16, 0}, + {"snow", (void *) LXB_CSS_VALUE_SNOW, 4, 280}, + {"small", (void *) LXB_CSS_VALUE_SMALL, 5, 0}, + {"sienna", (void *) LXB_CSS_VALUE_SIENNA, 6, 283}, + {"table-column", (void *) LXB_CSS_VALUE_TABLE_COLUMN, 12, 0}, + {"seashell", (void *) LXB_CSS_VALUE_SEASHELL, 8, 286}, + {"lightslategray", (void *) LXB_CSS_VALUE_LIGHTSLATEGRAY, 14, 287}, + {"ruby", (void *) LXB_CSS_VALUE_RUBY, 4, 0}, + {"sideways-rl", (void *) LXB_CSS_VALUE_SIDEWAYS_RL, 11, 0}, + {"all", (void *) LXB_CSS_VALUE_ALL, 3, 288}, + {"extra-condensed", (void *) LXB_CSS_VALUE_EXTRA_CONDENSED, 15, 0}, + {"indigo", (void *) LXB_CSS_VALUE_INDIGO, 6, 0}, + {"block-end", (void *) LXB_CSS_VALUE_BLOCK_END, 9, 134}, + {"bolder", (void *) LXB_CSS_VALUE_BOLDER, 6, 0}, + {"forestgreen", (void *) LXB_CSS_VALUE_FORESTGREEN, 11, 0}, + {"burlywood", (void *) LXB_CSS_VALUE_BURLYWOOD, 9, 0}, + {"blanchedalmond", (void *) LXB_CSS_VALUE_BLANCHEDALMOND, 14, 0}, + {"mediumvioletred", (void *) LXB_CSS_VALUE_MEDIUMVIOLETRED, 15, 0}, + {"papayawhip", (void *) LXB_CSS_VALUE_PAPAYAWHIP, 10, 0}, + {"buttonborder", (void *) LXB_CSS_VALUE_BUTTONBORDER, 12, 0}, + {"nowrap", (void *) LXB_CSS_VALUE_NOWRAP, 6, 0}, + {"double", (void *) LXB_CSS_VALUE_DOUBLE, 6, 141}, + {"smaller", (void *) LXB_CSS_VALUE_SMALLER, 7, 142}, + {"cornsilk", (void *) LXB_CSS_VALUE_CORNSILK, 8, 0}, + {"dodgerblue", (void *) LXB_CSS_VALUE_DODGERBLUE, 10, 144}, + {"sideways-lr", (void *) LXB_CSS_VALUE_SIDEWAYS_LR, 11, 0}, + {"deepskyblue", (void *) LXB_CSS_VALUE_DEEPSKYBLUE, 11, 0}, + {"skyblue", (void *) LXB_CSS_VALUE_SKYBLUE, 7, 0}, + {"gainsboro", (void *) LXB_CSS_VALUE_GAINSBORO, 9, 0}, + {"khaki", (void *) LXB_CSS_VALUE_KHAKI, 5, 290}, + {"firebrick", (void *) LXB_CSS_VALUE_FIREBRICK, 9, 153}, + {"snap-inline", (void *) LXB_CSS_VALUE_SNAP_INLINE, 11, 0}, + {"dotted", (void *) LXB_CSS_VALUE_DOTTED, 6, 293}, + {"flow", (void *) LXB_CSS_VALUE_FLOW, 4, 295}, + {"darkslateblue", (void *) LXB_CSS_VALUE_DARKSLATEBLUE, 13, 154}, + {"darkturquoise", (void *) LXB_CSS_VALUE_DARKTURQUOISE, 13, 0}, + {"hwb", (void *) LXB_CSS_VALUE_HWB, 3, 297}, + {"highlight", (void *) LXB_CSS_VALUE_HIGHLIGHT, 9, 0}, + {"palegoldenrod", (void *) LXB_CSS_VALUE_PALEGOLDENROD, 13, 159}, + {"visible", (void *) LXB_CSS_VALUE_VISIBLE, 7, 298}, + {"palevioletred", (void *) LXB_CSS_VALUE_PALEVIOLETRED, 13, 0}, + {"green", (void *) LXB_CSS_VALUE_GREEN, 5, 299}, + {"hidden", (void *) LXB_CSS_VALUE_HIDDEN, 6, 300}, + {"emoji", (void *) LXB_CSS_VALUE_EMOJI, 5, 301}, + {"darkgray", (void *) LXB_CSS_VALUE_DARKGRAY, 8, 303}, + {"tomato", (void *) LXB_CSS_VALUE_TOMATO, 6, 304}, + {"xx-large", (void *) LXB_CSS_VALUE_XX_LARGE, 8, 305}, + {"fangsong", (void *) LXB_CSS_VALUE_FANGSONG, 8, 306}, + {"start", (void *) LXB_CSS_VALUE_START, 5, 0}, + {"right", (void *) LXB_CSS_VALUE_RIGHT, 5, 307}, + {"revert", (void *) LXB_CSS_VALUE_REVERT, 6, 0}, + {"wavy", (void *) LXB_CSS_VALUE_WAVY, 4, 0}, + {"rgba", (void *) LXB_CSS_VALUE_RGBA, 4, 310}, + {"maroon", (void *) LXB_CSS_VALUE_MAROON, 6, 313}, + {"olive", (void *) LXB_CSS_VALUE_OLIVE, 5, 314}, + {"orange", (void *) LXB_CSS_VALUE_ORANGE, 6, 315}, + {"oldlace", (void *) LXB_CSS_VALUE_OLDLACE, 7, 317}, + {"overline", (void *) LXB_CSS_VALUE_OVERLINE, 8, 320}, + {"pre", (void *) LXB_CSS_VALUE_PRE, 3, 322}, + {"lch", (void *) LXB_CSS_VALUE_LCH, 3, 323}, + {"ruby-base", (void *) LXB_CSS_VALUE_RUBY_BASE, 9, 0}, + {"purple", (void *) LXB_CSS_VALUE_PURPLE, 6, 325}, + {"rtl", (void *) LXB_CSS_VALUE_RTL, 3, 328}, + {"bottom", (void *) LXB_CSS_VALUE_BOTTOM, 6, 329}, + {"flex", (void *) LXB_CSS_VALUE_FLEX, 4, 331}, + {"powderblue", (void *) LXB_CSS_VALUE_POWDERBLUE, 10, 0}, + {"ltr", (void *) LXB_CSS_VALUE_LTR, 3, 334}, + {"ultra-condensed", (void *) LXB_CSS_VALUE_ULTRA_CONDENSED, 15, 0}, + {"line-through", (void *) LXB_CSS_VALUE_LINE_THROUGH, 12, 336}, + {"sub", (void *) LXB_CSS_VALUE_SUB, 3, 337}, + {"lighter", (void *) LXB_CSS_VALUE_LIGHTER, 7, 340}, + {"lavender", (void *) LXB_CSS_VALUE_LAVENDER, 8, 341}, + {"inline-grid", (void *) LXB_CSS_VALUE_INLINE_GRID, 11, 0}, + {"row", (void *) LXB_CSS_VALUE_ROW, 3, 343}, + {"row-reverse", (void *) LXB_CSS_VALUE_ROW_REVERSE, 11, 0}, + {"rebeccapurple", (void *) LXB_CSS_VALUE_REBECCAPURPLE, 13, 0}, + {"content", (void *) LXB_CSS_VALUE_CONTENT, 7, 344}, + {"red", (void *) LXB_CSS_VALUE_RED, 3, 0}, + {"selecteditem", (void *) LXB_CSS_VALUE_SELECTEDITEM, 12, 0}, + {"oklch", (void *) LXB_CSS_VALUE_OKLCH, 5, 346}, + {"table", (void *) LXB_CSS_VALUE_TABLE, 5, 348}, + {"mark", (void *) LXB_CSS_VALUE_MARK, 4, 0}, + {"black", (void *) LXB_CSS_VALUE_BLACK, 5, 349}, + {"inline-block", (void *) LXB_CSS_VALUE_INLINE_BLOCK, 12, 0}, + {"clip", (void *) LXB_CSS_VALUE_CLIP, 4, 352}, + {"anywhere", (void *) LXB_CSS_VALUE_ANYWHERE, 8, 205}, + {"absolute", (void *) LXB_CSS_VALUE_ABSOLUTE, 8, 0}, + {"wheat", (void *) LXB_CSS_VALUE_WHEAT, 5, 0}, + {"underline", (void *) LXB_CSS_VALUE_UNDERLINE, 9, 353}, + {"top", (void *) LXB_CSS_VALUE_TOP, 3, 0}, + {"alphabetic", (void *) LXB_CSS_VALUE_ALPHABETIC, 10, 0}, + {"ui-monospace", (void *) LXB_CSS_VALUE_UI_MONOSPACE, 12, 0}, + {"darkkhaki", (void *) LXB_CSS_VALUE_DARKKHAKI, 9, 0}, + {"graytext", (void *) LXB_CSS_VALUE_GRAYTEXT, 8, 214}, + {"serif", (void *) LXB_CSS_VALUE_SERIF, 5, 354}, + {"list-item", (void *) LXB_CSS_VALUE_LIST_ITEM, 9, 0}, + {"coral", (void *) LXB_CSS_VALUE_CORAL, 5, 355}, + {"ellipsis", (void *) LXB_CSS_VALUE_ELLIPSIS, 8, 0}, + {"central", (void *) LXB_CSS_VALUE_CENTRAL, 7, 357}, + {"darkcyan", (void *) LXB_CSS_VALUE_DARKCYAN, 8, 358}, + {"hsla", (void *) LXB_CSS_VALUE_HSLA, 4, 359}, + {"darksalmon", (void *) LXB_CSS_VALUE_DARKSALMON, 10, 360}, + {"flow-root", (void *) LXB_CSS_VALUE_FLOW_ROOT, 9, 0}, + {"darkseagreen", (void *) LXB_CSS_VALUE_DARKSEAGREEN, 12, 0}, + {"table-header-group", (void *) LXB_CSS_VALUE_TABLE_HEADER_GROUP, 18, 361}, + {"darkolivegreen", (void *) LXB_CSS_VALUE_DARKOLIVEGREEN, 14, 0}, + {"gray", (void *) LXB_CSS_VALUE_GRAY, 4, 363}, + {"_angle", (void *) LXB_CSS_VALUE__ANGLE, 6, 0}, + {"ghostwhite", (void *) LXB_CSS_VALUE_GHOSTWHITE, 10, 229}, + {"thick", (void *) LXB_CSS_VALUE_THICK, 5, 0}, + {"wrap-reverse", (void *) LXB_CSS_VALUE_WRAP_REVERSE, 12, 0}, + {"math", (void *) LXB_CSS_VALUE_MATH, 4, 365}, + {"_length", (void *) LXB_CSS_VALUE__LENGTH, 7, 366}, + {"fuchsia", (void *) LXB_CSS_VALUE_FUCHSIA, 7, 0}, + {"wrap", (void *) LXB_CSS_VALUE_WRAP, 4, 367}, + {"fixed", (void *) LXB_CSS_VALUE_FIXED, 5, 235}, + {"lightyellow", (void *) LXB_CSS_VALUE_LIGHTYELLOW, 11, 0}, + {"clear", (void *) LXB_CSS_VALUE_CLEAR, 5, 237}, + {"deeppink", (void *) LXB_CSS_VALUE_DEEPPINK, 8, 0}, + {"last", (void *) LXB_CSS_VALUE_LAST, 4, 368}, + {"full-size-kana", (void *) LXB_CSS_VALUE_FULL_SIZE_KANA, 14, 0}, + {"flex-end", (void *) LXB_CSS_VALUE_FLEX_END, 8, 0}, + {"palegreen", (void *) LXB_CSS_VALUE_PALEGREEN, 9, 370}, + {"linktext", (void *) LXB_CSS_VALUE_LINKTEXT, 8, 0}, + {"text-bottom", (void *) LXB_CSS_VALUE_TEXT_BOTTOM, 11, 245}, + {"inset", (void *) LXB_CSS_VALUE_INSET, 5, 0}, + {"inline-flex", (void *) LXB_CSS_VALUE_INLINE_FLEX, 11, 0}, + {"inherit", (void *) LXB_CSS_VALUE_INHERIT, 7, 0}, + {"navy", (void *) LXB_CSS_VALUE_NAVY, 4, 0}, + {"grid", (void *) LXB_CSS_VALUE_GRID, 4, 252}, + {"ruby-base-container", (void *) LXB_CSS_VALUE_RUBY_BASE_CONTAINER, 19, 371}, + {"orchid", (void *) LXB_CSS_VALUE_ORCHID, 6, 0}, + {"inline-start", (void *) LXB_CSS_VALUE_INLINE_START, 12, 0}, + {"border-box", (void *) LXB_CSS_VALUE_BORDER_BOX, 10, 0}, + {"peru", (void *) LXB_CSS_VALUE_PERU, 4, 372}, + {"currentcolor", (void *) LXB_CSS_VALUE_CURRENTCOLOR, 12, 255}, + {"lightgoldenrodyellow", (void *) LXB_CSS_VALUE_LIGHTGOLDENRODYELLOW, 20, 0}, + {"space-around", (void *) LXB_CSS_VALUE_SPACE_AROUND, 12, 0}, + {"semi-expanded", (void *) LXB_CSS_VALUE_SEMI_EXPANDED, 13, 0}, + {"semi-condensed", (void *) LXB_CSS_VALUE_SEMI_CONDENSED, 14, 0}, + {"inline-table", (void *) LXB_CSS_VALUE_INLINE_TABLE, 12, 0}, + {"lightcyan", (void *) LXB_CSS_VALUE_LIGHTCYAN, 9, 261}, + {"limegreen", (void *) LXB_CSS_VALUE_LIMEGREEN, 9, 0}, + {"lightsalmon", (void *) LXB_CSS_VALUE_LIGHTSALMON, 11, 263}, + {"isolate-override", (void *) LXB_CSS_VALUE_ISOLATE_OVERRIDE, 16, 0}, + {"keep-all", (void *) LXB_CSS_VALUE_KEEP_ALL, 8, 265}, + {"lemonchiffon", (void *) LXB_CSS_VALUE_LEMONCHIFFON, 12, 0}, + {"sideways", (void *) LXB_CSS_VALUE_SIDEWAYS, 8, 0}, + {"large", (void *) LXB_CSS_VALUE_LARGE, 5, 268}, + {"loose", (void *) LXB_CSS_VALUE_LOOSE, 5, 269}, + {"honeydew", (void *) LXB_CSS_VALUE_HONEYDEW, 8, 0}, + {"lowercase", (void *) LXB_CSS_VALUE_LOWERCASE, 9, 0}, + {"floralwhite", (void *) LXB_CSS_VALUE_FLORALWHITE, 11, 0}, + {"lightsteelblue", (void *) LXB_CSS_VALUE_LIGHTSTEELBLUE, 14, 0}, + {"marktext", (void *) LXB_CSS_VALUE_MARKTEXT, 8, 0}, + {"none", (void *) LXB_CSS_VALUE_NONE, 4, 0}, + {"buttontext", (void *) LXB_CSS_VALUE_BUTTONTEXT, 10, 0}, + {"region", (void *) LXB_CSS_VALUE_REGION, 6, 277}, + {"block-start", (void *) LXB_CSS_VALUE_BLOCK_START, 11, 278}, + {"min-content", (void *) LXB_CSS_VALUE_MIN_CONTENT, 11, 279}, + {"max-content", (void *) LXB_CSS_VALUE_MAX_CONTENT, 11, 0}, + {"lightgray", (void *) LXB_CSS_VALUE_LIGHTGRAY, 9, 281}, + {"lightgrey", (void *) LXB_CSS_VALUE_LIGHTGREY, 9, 282}, + {"rosybrown", (void *) LXB_CSS_VALUE_ROSYBROWN, 9, 0}, + {"scroll", (void *) LXB_CSS_VALUE_SCROLL, 6, 284}, + {"digits", (void *) LXB_CSS_VALUE_DIGITS, 6, 285}, + {"navajowhite", (void *) LXB_CSS_VALUE_NAVAJOWHITE, 11, 0}, + {"table-caption", (void *) LXB_CSS_VALUE_TABLE_CAPTION, 13, 0}, + {"lightslategrey", (void *) LXB_CSS_VALUE_LIGHTSLATEGREY, 14, 0}, + {"yellow", (void *) LXB_CSS_VALUE_YELLOW, 6, 289}, + {"break-spaces", (void *) LXB_CSS_VALUE_BREAK_SPACES, 12, 0}, + {"oklab", (void *) LXB_CSS_VALUE_OKLAB, 5, 291}, + {"slateblue", (void *) LXB_CSS_VALUE_SLATEBLUE, 9, 292}, + {"steelblue", (void *) LXB_CSS_VALUE_STEELBLUE, 9, 0}, + {"dashed", (void *) LXB_CSS_VALUE_DASHED, 6, 294}, + {"transparent", (void *) LXB_CSS_VALUE_TRANSPARENT, 11, 0}, + {"darkred", (void *) LXB_CSS_VALUE_DARKRED, 7, 296}, + {"olivedrab", (void *) LXB_CSS_VALUE_OLIVEDRAB, 9, 0}, + {"darkorchid", (void *) LXB_CSS_VALUE_DARKORCHID, 10, 0}, + {"darkgoldenrod", (void *) LXB_CSS_VALUE_DARKGOLDENROD, 13, 0}, + {"_number", (void *) LXB_CSS_VALUE__NUMBER, 7, 0}, + {"_integer", (void *) LXB_CSS_VALUE__INTEGER, 8, 0}, + {"dimgray", (void *) LXB_CSS_VALUE_DIMGRAY, 7, 302}, + {"dimgrey", (void *) LXB_CSS_VALUE_DIMGREY, 7, 0}, + {"darkgrey", (void *) LXB_CSS_VALUE_DARKGREY, 8, 0}, + {"x-large", (void *) LXB_CSS_VALUE_X_LARGE, 7, 0}, + {"horizontal-tb", (void *) LXB_CSS_VALUE_HORIZONTAL_TB, 13, 0}, + {"xxx-large", (void *) LXB_CSS_VALUE_XXX_LARGE, 9, 0}, + {"strict", (void *) LXB_CSS_VALUE_STRICT, 6, 308}, + {"darkslategray", (void *) LXB_CSS_VALUE_DARKSLATEGRAY, 13, 309}, + {"darkslategrey", (void *) LXB_CSS_VALUE_DARKSLATEGREY, 13, 0}, + {"brown", (void *) LXB_CSS_VALUE_BROWN, 5, 311}, + {"system-ui", (void *) LXB_CSS_VALUE_SYSTEM_UI, 9, 312}, + {"accentcolor", (void *) LXB_CSS_VALUE_ACCENTCOLOR, 11, 0}, + {"ruby-text", (void *) LXB_CSS_VALUE_RUBY_TEXT, 9, 0}, + {"cursive", (void *) LXB_CSS_VALUE_CURSIVE, 7, 0}, + {"moccasin", (void *) LXB_CSS_VALUE_MOCCASIN, 8, 316}, + {"collapse", (void *) LXB_CSS_VALUE_COLLAPSE, 8, 0}, + {"oblique", (void *) LXB_CSS_VALUE_OBLIQUE, 7, 318}, + {"cadetblue", (void *) LXB_CSS_VALUE_CADETBLUE, 9, 319}, + {"chocolate", (void *) LXB_CSS_VALUE_CHOCOLATE, 9, 0}, + {"chartreuse", (void *) LXB_CSS_VALUE_CHARTREUSE, 10, 321}, + {"capitalize", (void *) LXB_CSS_VALUE_CAPITALIZE, 10, 0}, + {"yellowgreen", (void *) LXB_CSS_VALUE_YELLOWGREEN, 11, 0}, + {"page", (void *) LXB_CSS_VALUE_PAGE, 4, 324}, + {"selecteditemtext", (void *) LXB_CSS_VALUE_SELECTEDITEMTEXT, 16, 0}, + {"cornflowerblue", (void *) LXB_CSS_VALUE_CORNFLOWERBLUE, 14, 326}, + {"mediumseagreen", (void *) LXB_CSS_VALUE_MEDIUMSEAGREEN, 14, 327}, + {"column-reverse", (void *) LXB_CSS_VALUE_COLUMN_REVERSE, 14, 0}, + {"ui-rounded", (void *) LXB_CSS_VALUE_UI_ROUNDED, 10, 0}, + {"medium", (void *) LXB_CSS_VALUE_MEDIUM, 6, 330}, + {"pre-line", (void *) LXB_CSS_VALUE_PRE_LINE, 8, 0}, + {"minimum", (void *) LXB_CSS_VALUE_MINIMUM, 7, 332}, + {"maximum", (void *) LXB_CSS_VALUE_MAXIMUM, 7, 333}, + {"mediumspringgreen", (void *) LXB_CSS_VALUE_MEDIUMSPRINGGREEN, 17, 0}, + {"mintcream", (void *) LXB_CSS_VALUE_MINTCREAM, 9, 335}, + {"ultra-expanded", (void *) LXB_CSS_VALUE_ULTRA_EXPANDED, 14, 0}, + {"paleturquoise", (void *) LXB_CSS_VALUE_PALETURQUOISE, 13, 0}, + {"larger", (void *) LXB_CSS_VALUE_LARGER, 6, 338}, + {"content-box", (void *) LXB_CSS_VALUE_CONTENT_BOX, 11, 339}, + {"lavenderblush", (void *) LXB_CSS_VALUE_LAVENDERBLUSH, 13, 0}, + {"indianred", (void *) LXB_CSS_VALUE_INDIANRED, 9, 0}, + {"inline-end", (void *) LXB_CSS_VALUE_INLINE_END, 10, 342}, + {"inter-word", (void *) LXB_CSS_VALUE_INTER_WORD, 10, 0}, + {"manual", (void *) LXB_CSS_VALUE_MANUAL, 6, 0}, + {"justify", (void *) LXB_CSS_VALUE_JUSTIFY, 7, 345}, + {"break-all", (void *) LXB_CSS_VALUE_BREAK_ALL, 9, 0}, + {"canvastext", (void *) LXB_CSS_VALUE_CANVASTEXT, 10, 347}, + {"mathematical", (void *) LXB_CSS_VALUE_MATHEMATICAL, 12, 0}, + {"full-width", (void *) LXB_CSS_VALUE_FULL_WIDTH, 10, 0}, + {"block", (void *) LXB_CSS_VALUE_BLOCK, 5, 350}, + {"blink", (void *) LXB_CSS_VALUE_BLINK, 5, 351}, + {"thistle", (void *) LXB_CSS_VALUE_THISTLE, 7, 0}, + {"turquoise", (void *) LXB_CSS_VALUE_TURQUOISE, 9, 0}, + {"uppercase", (void *) LXB_CSS_VALUE_UPPERCASE, 9, 0}, + {"text-top", (void *) LXB_CSS_VALUE_TEXT_TOP, 8, 0}, + {"ivory", (void *) LXB_CSS_VALUE_IVORY, 5, 356}, + {"inter-character", (void *) LXB_CSS_VALUE_INTER_CHARACTER, 15, 0}, + {"fantasy", (void *) LXB_CSS_VALUE_FANTASY, 7, 0}, + {"sans-serif", (void *) LXB_CSS_VALUE_SANS_SERIF, 10, 0}, + {"darkgreen", (void *) LXB_CSS_VALUE_DARKGREEN, 9, 0}, + {"table-row-group", (void *) LXB_CSS_VALUE_TABLE_ROW_GROUP, 15, 0}, + {"table-footer-group", (void *) LXB_CSS_VALUE_TABLE_FOOTER_GROUP, 18, 362}, + {"table-column-group", (void *) LXB_CSS_VALUE_TABLE_COLUMN_GROUP, 18, 0}, + {"grey", (void *) LXB_CSS_VALUE_GREY, 4, 364}, + {"vertical-rl", (void *) LXB_CSS_VALUE_VERTICAL_RL, 11, 0}, + {"both", (void *) LXB_CSS_VALUE_BOTH, 4, 0}, + {"_percentage", (void *) LXB_CSS_VALUE__PERCENTAGE, 11, 0}, + {"ui-serif", (void *) LXB_CSS_VALUE_UI_SERIF, 8, 0}, + {"left", (void *) LXB_CSS_VALUE_LEFT, 4, 369}, + {"ui-sans-serif", (void *) LXB_CSS_VALUE_UI_SANS_SERIF, 13, 0}, + {"condensed", (void *) LXB_CSS_VALUE_CONDENSED, 9, 0}, + {"ruby-text-container", (void *) LXB_CSS_VALUE_RUBY_TEXT_CONTAINER, 19, 0}, + {"orangered", (void *) LXB_CSS_VALUE_ORANGERED, 9, 0} +}; + + +#endif /* LXB_CSS_VALUE_RES_H */ diff --git a/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/ports/posix/lexbor/core/memory.c b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/ports/posix/lexbor/core/memory.c new file mode 100644 index 0000000..038e040 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/lexbor/lexbor/ports/posix/lexbor/core/memory.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2018 Alexander Borisov + * + * Author: Alexander Borisov + */ + +#include "lexbor/core/base.h" + +static lexbor_memory_malloc_f lexbor_memory_malloc = malloc; +static lexbor_memory_realloc_f lexbor_memory_realloc = realloc; +static lexbor_memory_calloc_f lexbor_memory_calloc = calloc; +static lexbor_memory_free_f lexbor_memory_free = free; + +void * +lexbor_malloc(size_t size) +{ + return lexbor_memory_malloc(size); +} + +void * +lexbor_realloc(void *dst, size_t size) +{ + return lexbor_memory_realloc(dst, size); +} + +void * +lexbor_calloc(size_t num, size_t size) +{ + return lexbor_memory_calloc(num, size); +} + +void * +lexbor_free(void *dst) +{ + lexbor_memory_free(dst); + return NULL; +} + +lxb_status_t +lexbor_memory_setup(lexbor_memory_malloc_f new_malloc, lexbor_memory_realloc_f new_realloc, + lexbor_memory_calloc_f new_calloc, lexbor_memory_free_f new_free) +{ + if (new_malloc == NULL || new_realloc == NULL || new_calloc == NULL || new_free == NULL) { + return LXB_STATUS_ERROR_OBJECT_IS_NULL; + } + + lexbor_memory_malloc = new_malloc; + lexbor_memory_realloc = new_realloc; + lexbor_memory_calloc = new_calloc; + lexbor_memory_free = new_free; + + return LXB_STATUS_OK; +} diff --git a/experiments/css/crates/css-compiler/vendor/stubs/inttypes.h b/experiments/css/crates/css-compiler/vendor/stubs/inttypes.h new file mode 100644 index 0000000..4c31f17 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/inttypes.h @@ -0,0 +1,4 @@ +#ifndef INTTYPES_H +#define INTTYPES_H +#include +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/math.h b/experiments/css/crates/css-compiler/vendor/stubs/math.h new file mode 100644 index 0000000..a7a99fc --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/math.h @@ -0,0 +1,12 @@ +#ifndef MATH_H +#define MATH_H +#define INFINITY (__builtin_inff()) +#define NAN (__builtin_nanf("")) +#define signbit(x) (__builtin_signbit(x)) +double pow(double, double); +double fmod(double, double); +double floor(double); +double ceil(double); +double round(double); +double log10(double); +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/memory.h b/experiments/css/crates/css-compiler/vendor/stubs/memory.h new file mode 100644 index 0000000..79418b8 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/memory.h @@ -0,0 +1,4 @@ +#ifndef MEMORY_H +#define MEMORY_H +#include +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/stdbool.h b/experiments/css/crates/css-compiler/vendor/stubs/stdbool.h new file mode 100644 index 0000000..81afced --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/stdbool.h @@ -0,0 +1,6 @@ +#ifndef STDBOOL_H +#define STDBOOL_H +#define bool _Bool +#define true 1 +#define false 0 +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/stdio.h b/experiments/css/crates/css-compiler/vendor/stubs/stdio.h new file mode 100644 index 0000000..acee9e0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/stdio.h @@ -0,0 +1,5 @@ +#ifndef STDIO_H +#define STDIO_H +int printf(const char *, ...); +int snprintf(char *, unsigned long, const char *, ...); +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/stdlib.h b/experiments/css/crates/css-compiler/vendor/stubs/stdlib.h new file mode 100644 index 0000000..0f70d4b --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/stdlib.h @@ -0,0 +1,9 @@ +#ifndef STDLIB_H +#define STDLIB_H +void *malloc(unsigned long); +void *calloc(unsigned long, unsigned long); +void *realloc(void *, unsigned long); +void free(void *); +void abort(void); +void exit(int); +#endif diff --git a/experiments/css/crates/css-compiler/vendor/stubs/string.h b/experiments/css/crates/css-compiler/vendor/stubs/string.h new file mode 100644 index 0000000..3389ea0 --- /dev/null +++ b/experiments/css/crates/css-compiler/vendor/stubs/string.h @@ -0,0 +1,14 @@ +#ifndef STRING_H +#define STRING_H +void *memcpy(void *, const void *, unsigned long); +void *memmove(void *, const void *, unsigned long); +void *memset(void *, int, unsigned long); +int memcmp(const void *, const void *, unsigned long); +unsigned long strlen(const char *); +int strcmp(const char *, const char *); +int strncmp(const char *, const char *, unsigned long); +char *strchr(const char *, int); +void *memchr(const void *, int, unsigned long); +char *strcpy(char *, const char *); +char *strncpy(char *, const char *, unsigned long); +#endif diff --git a/experiments/css/crates/css-conformance/Cargo.toml b/experiments/css/crates/css-conformance/Cargo.toml new file mode 100644 index 0000000..3d34db1 --- /dev/null +++ b/experiments/css/crates/css-conformance/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "bombshell-css-conformance" +version = "0.1.0" +description = "Conformance corpus for the Bombshell CSS pipeline" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +publish = false + +[dev-dependencies] +bombshell-css-core = { workspace = true } +bombshell-css-compiler = { workspace = true } +bombshell-css-runtime = { workspace = true } diff --git a/experiments/css/crates/css-conformance/cases/cascade/01-layers.css b/experiments/css/crates/css-conformance/cases/cascade/01-layers.css new file mode 100644 index 0000000..3dd461c --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/cascade/01-layers.css @@ -0,0 +1,5 @@ +/* Cascade 5: layer declaration order, nested layers, unlayered beats layered. */ +@layer a, b.c; +@layer a { .x { color: red } } +@layer b.c { .x { color: green } } +.x { color: blue } diff --git a/experiments/css/crates/css-conformance/cases/cascade/02-important.css b/experiments/css/crates/css-conformance/cases/cascade/02-important.css new file mode 100644 index 0000000..8857d8b --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/cascade/02-important.css @@ -0,0 +1,3 @@ +/* Importance and origin interactions (evaluated across origins by the runtime). */ +.x { color: red !important } +.x { color: green } diff --git a/experiments/css/crates/css-conformance/cases/colors/01-modern.css b/experiments/css/crates/css-conformance/cases/colors/01-modern.css new file mode 100644 index 0000000..7fb6b8f --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/colors/01-modern.css @@ -0,0 +1,12 @@ +/* CSS Color 4/5: wide-gamut spaces, mixing, relative syntax, light-dark(). + Values are validated and canonicalized by lightningcss; colors are not + converted or gamut-mapped by the runtime yet. */ +.a { color: oklch(70% .2 40) } +.b { color: oklab(0.5 -0.1 0.1 / 50%) } +.c { color: lab(50% 20 -30) } +.d { color: lch(50% 40 120deg) } +.e { color: color(display-p3 1 0.5 0) } +.f { color: color-mix(in oklab, red 40%, blue) } +.g { color: rgb(from orange r g b / 50%) } +.h { color: light-dark(white, black) } +.i { color: hsl(120deg 75% 50%) } diff --git a/experiments/css/crates/css-conformance/cases/colors/02-hex.css b/experiments/css/crates/css-conformance/cases/colors/02-hex.css new file mode 100644 index 0000000..2d09f5b --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/colors/02-hex.css @@ -0,0 +1,12 @@ +/* Plain hex colors in every digit-count form, across the properties the + report called out. Hex payloads that read as identifiers (#fff) tokenize + as IDHash; digit-leading payloads (#333) tokenize as Hash — both must + reach the IR and the computed style. */ +.a { color: #fff } +.b { color: #ff8800 } +.c { color: #ffffff } +.d { color: #fffe } +.e { color: #ff8800ff } +.f { border-color: #333 } +.g { border-color: #333333 } +.h { background-color: #101010 } \ No newline at end of file diff --git a/experiments/css/crates/css-conformance/cases/conditions/01-media.css b/experiments/css/crates/css-conformance/cases/conditions/01-media.css new file mode 100644 index 0000000..968fa48 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/conditions/01-media.css @@ -0,0 +1,6 @@ +/* Media Queries Level 4: range syntax, boolean and discrete features, OR lists. */ +@media (400px <= width <= 800px) { .a { color: red } } +@media (prefers-color-scheme: dark) { .a { color: green } } +@media (prefers-reduced-motion: reduce) { .a { color: blue } } +@media screen and (not (hover)) { .a { color: orange } } +@media (min-width: 400px), (max-height: 10em) { .a { color: purple } } diff --git a/experiments/css/crates/css-conformance/cases/conditions/02-container.css b/experiments/css/crates/css-conformance/cases/conditions/02-container.css new file mode 100644 index 0000000..6002756 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/conditions/02-container.css @@ -0,0 +1,7 @@ +/* CSS Containment 3: named/anonymous containers, range and boolean size queries. */ +.panel { container-type: inline-size; container-name: panel } +@container panel (inline-size > 45em) { .a { color: red } } +@container (width < 200px) { .a { color: green } } +@container card (height >= 10em) { .a { color: blue } } +@container (aspect-ratio > 5) { .a { color: orange } } +@container panel style(color: red) { .a { color: purple } } diff --git a/experiments/css/crates/css-conformance/cases/conditions/03-supports.css b/experiments/css/crates/css-conformance/cases/conditions/03-supports.css new file mode 100644 index 0000000..14bd463 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/conditions/03-supports.css @@ -0,0 +1,5 @@ +/* Conditional rules: @supports declarations, selector(), and/or/not algebra. */ +@supports (color: oklch(70% .2 40)) { .a { color: red } } +@supports selector(:has(> .b)) { .a { color: green } } +@supports not (bogus-property: 1px) { .a { color: blue } } +@supports (display: grid) and (color: red) { .a { color: orange } } diff --git a/experiments/css/crates/css-conformance/cases/nesting/01-basic.css b/experiments/css/crates/css-conformance/cases/nesting/01-basic.css new file mode 100644 index 0000000..5a5c1a5 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/nesting/01-basic.css @@ -0,0 +1,13 @@ +/* CSS Nesting: implicit descendant nesting and explicit `&`. + Structure adapted from CSS Nesting Module (csswg-drafts/css-nesting-1) examples. */ +.card { + color: oklch(70% .2 40); + + & > .title { + font-weight: bold; + } + + .body { + color: color-mix(in oklab, red 40%, blue); + } +} diff --git a/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css b/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css new file mode 100644 index 0000000..f3930b8 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css @@ -0,0 +1,10 @@ +/* CSS Nesting: multi-level expansion with mid-compound `&` and sibling context. */ +.a { + .b { + &.c { + :not(&) { + outline: 1px; + } + } + } +} diff --git a/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css b/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css new file mode 100644 index 0000000..f0e081f --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css @@ -0,0 +1,12 @@ +/* CSS Nesting: group rules inside style rules keep the nesting context. */ +.panel { + padding: 4px; + + @media (min-width: 24em) { + .item { color: red } + } + + @container (inline-size > 20em) { + .item { color: blue } + } +} diff --git a/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css b/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css new file mode 100644 index 0000000..a13867b --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css @@ -0,0 +1,8 @@ +/* Legacy nesting via @nest (requires an explicit & prefix). */ +.a { + color: red; + + @nest & .b { + color: blue; + } +} diff --git a/experiments/css/crates/css-conformance/cases/selectors/01-level4.css b/experiments/css/crates/css-conformance/cases/selectors/01-level4.css new file mode 100644 index 0000000..00f091b --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/selectors/01-level4.css @@ -0,0 +1,8 @@ +/* Selectors Level 4: :is, :where, :not (complex args), :has, :nth-child(An+B of S). */ +:is(.a, .b) > .c { color: red } +:where(#x, .y) .z { color: green } +.item:not(.skip):not(.list > *) { color: blue } +.row:has(> .col) { outline: 1px } +.item:nth-child(2n + 1 of .tagged) { color: yellow } +.item:nth-child(-n + 3) { text-indent: 4px } +p:has(+ h2) { margin: 4px } diff --git a/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css b/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css new file mode 100644 index 0000000..4ba8ffc --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css @@ -0,0 +1,7 @@ +/* Selectors Level 4 attribute matching, including case flags. */ +[data-x^='pre'] { color: red } +[title*='word' i] { color: green } +[href$='.pdf'] { color: blue } +[lang|='en'] { color: orange } +[data-y~='item' s] { color: purple } +[hidden] { visibility: hidden } diff --git a/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css b/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css new file mode 100644 index 0000000..13eb6e1 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css @@ -0,0 +1,4 @@ +/* Descendant, child, next-sibling, later-sibling, and mixed chains. */ +.a .b .c { color: red } +.a > .b + .c ~ .d { color: green } +*.item { color: blue } diff --git a/experiments/css/crates/css-conformance/cases/values/01-var.css b/experiments/css/crates/css-conformance/cases/values/01-var.css new file mode 100644 index 0000000..adb7e7e --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/values/01-var.css @@ -0,0 +1,5 @@ +/* Custom properties, var() with fallbacks, substitution through nesting. */ +:root { --brand: oklch(70% .2 40); --gap: 8px } +.a { color: var(--brand); margin: var(--gap, 4px) } +.b { --gap: 2px; padding: var(--gap) } +.c { color: var(--missing, var(--brand)) } diff --git a/experiments/css/crates/css-conformance/cases/values/02-single-line.css b/experiments/css/crates/css-conformance/cases/values/02-single-line.css new file mode 100644 index 0000000..48db531 --- /dev/null +++ b/experiments/css/crates/css-conformance/cases/values/02-single-line.css @@ -0,0 +1,7 @@ +/* Single-line (minified) stylesheets: a rule body that opens and closes on + the same line must survive the shim's event stream, including comma + selectors, nesting, and grouping at-rules. */ +.card { color: #fff; padding: 4px } +.a,.b { color: red } +.x { & > .y { color: blue } } +@media (min-width: 24em) { .z { color: green } } \ No newline at end of file diff --git a/experiments/css/crates/css-conformance/tests/behavior.rs b/experiments/css/crates/css-conformance/tests/behavior.rs new file mode 100644 index 0000000..47e0f16 --- /dev/null +++ b/experiments/css/crates/css-conformance/tests/behavior.rs @@ -0,0 +1,360 @@ +//! Behavior conformance: compile corpus cases and resolve computed styles +//! against tree snapshots, asserting the cascade semantics the corpus pins. + +use std::fs; +use std::path::Path; + +use bombshell_css_compiler::{CompileOptions, compile}; +use bombshell_css_core::{ColorScheme, Environment, NO_PARENT, NodeSnapshot, Origin, TreeSnapshot}; +use bombshell_css_runtime::{Stylesheet, compute}; + +fn read_case(rel: &str) -> String { + fs::read_to_string( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("cases") + .join(rel), + ) + .expect("case file") +} + +/// An owned compiled case; [`Case::sheet`] borrows the parsed stylesheet +/// from the IR it keeps alive. +struct Case { + ir: bombshell_css_core::CompiledStylesheet, +} + +impl Case { + fn sheet(&self) -> Stylesheet<'_> { + Stylesheet::from_ir(&self.ir, 0) + } +} + +fn load(css: &str) -> Case { + let output = compile(css, Origin::Author, CompileOptions::default()); + assert!( + !output + .diagnostics + .iter() + .any(|d| d.severity == bombshell_css_core::Severity::Error), + "{:?}", + output.diagnostics + ); + Case { + ir: output.sheet.expect("sheet"), + } +} + +fn tree(nodes: Vec, parents: Vec) -> TreeSnapshot { + TreeSnapshot { nodes, parents } +} + +fn classed(tag: &str, classes: &[&str]) -> NodeSnapshot { + NodeSnapshot { + tag: tag.to_string(), + classes: classes.iter().map(|c| c.to_string()).collect(), + ..Default::default() + } +} + +fn get<'a>(style: &'a bombshell_css_runtime::ComputedStyle, name: &str) -> &'a str { + style + .get(name) + .unwrap_or_else(|| panic!("missing {name}: {:?}", style.declarations)) +} + +#[test] +fn nesting_case_styles_the_tree() { + let sheet = load(&read_case("nesting/01-basic.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![ + classed("card", &["card"]), + classed("title", &["title"]), + classed("body", &["body"]), + ], + vec![NO_PARENT, 0, 0], + ); + let env = Environment::default(); + let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s, "font-weight"), "700"); + // `color` inherits from .card; `.card .body` does not apply to the title. + assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); + let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + // lightningcss canonicalizes the static color-mix() to a resolved oklab. + let color = get(&s, "color"); + assert!( + color.starts_with("oklab(") || color.starts_with("color-mix("), + "unexpected color {color}" + ); + assert_ne!(color, "oklch(70% .2 40)"); +} + +#[test] +fn level4_selectors_match() { + let sheet = load(&read_case("selectors/01-level4.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![ + classed("row", &["row"]), + classed("col", &["col"]), + classed("item", &["item", "tagged"]), + classed("item", &["item"]), + ], + vec![NO_PARENT, 0, 0, 0], + ); + let env = Environment::default(); + // .row:has(> .col) matches the row. + let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&s, "outline"), "1px"); + // nth-child(odd of .tagged): node 2 is the 1st tagged child → matches. + let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + assert_eq!(get(&s, "color"), "#ff0"); + // node 3 is the 3rd child → nth-child(-n+3) applies (blue outranks it on + // `color` specificity, so the corpus rule uses a distinct property). + let s = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + assert_eq!(get(&s, "text-indent"), "4px"); +} + +#[test] +fn media_ranges_and_or_lists() { + let sheet = load(&read_case("conditions/01-media.css")); + let sheet = sheet.sheet(); + let t = tree(vec![classed("a", &["a"])], vec![NO_PARENT]); + let env = Environment { + viewport_width: Some(600.0), + viewport_height: Some(300.0), + ..Default::default() + }; + // The corpus stack: interval red, not-hover orange, and min-width purple + // all match at 600px; later source order wins. + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "purple" + ); + + // Interval semantics in isolation. + let interval = load("@media (400px <= width <= 800px) { .a { color: red } }"); + let interval = interval.sheet(); + assert_eq!( + get( + &compute(std::slice::from_ref(&interval), &t, 0, &env).unwrap(), + "color" + ), + "red" + ); + let small = Environment { + viewport_width: Some(300.0), + ..Default::default() + }; + assert!( + compute(std::slice::from_ref(&interval), &t, 0, &small) + .unwrap() + .get("color") + .is_none() + ); + let edge = Environment { + viewport_width: Some(800.0), + ..Default::default() + }; + assert_eq!( + get(&compute(&[interval], &t, 0, &edge).unwrap(), "color"), + "red" + ); + + // The OR list matches on max-height 10em (160px) alone. + let small_height = Environment { + viewport_width: Some(300.0), + viewport_height: Some(96.0), + ..Default::default() + }; + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &small_height).unwrap(), + "color" + ), + "purple" + ); +} + +#[test] +fn dark_scheme_reaches_the_runtime() { + let sheet = load(&read_case("conditions/01-media.css")); + let sheet = sheet.sheet(); + let t = tree(vec![classed("a", &["a"])], vec![NO_PARENT]); + let env = Environment { + color_scheme: ColorScheme::Dark, + ..Default::default() + }; + // Dark-scheme green matches, but `screen and (not (hover))` also matches + // and appears later in source order. + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "orange" + ); + + // Dark scheme in isolation. + let dark = load("@media (prefers-color-scheme: dark) { .a { color: green } }"); + let dark = dark.sheet(); + assert_eq!( + get( + &compute(std::slice::from_ref(&dark), &t, 0, &env).unwrap(), + "color" + ), + "green" + ); +} + +#[test] +fn container_corpus_case() { + let sheet = load(&read_case("conditions/02-container.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![ + { + let mut n = classed("panel", &["panel"]); + n.width = Some(800.0); + n.height = Some(200.0); + n + }, + classed("a", &["a"]), + ], + vec![NO_PARENT, 0], + ); + let env = Environment::default(); + let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + // inline-size 800 > 45em (720px) → red; width 800 > 200 → the anonymous + // container query would win too but it targets `(width < 200px)` → no. + assert_eq!(get(&s, "color"), "red"); +} + +#[test] +fn var_case_resolves_through_the_tree() { + let sheet = load(&read_case("values/01-var.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![ + classed("root", &["root"]), + classed("a", &["a"]), + classed("b", &["b"]), + classed("c", &["c"]), + ], + vec![NO_PARENT, 0, 0, 0], + ); + let env = Environment::default(); + let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); + assert_eq!(get(&s, "margin"), "8px"); + let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + assert_eq!(get(&s, "padding"), "2px"); + let s = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); +} + +#[test] +fn layer_order_matches_first_appearance() { + let sheet = load(&read_case("cascade/01-layers.css")); + let sheet = sheet.sheet(); + let t = tree(vec![classed("x", &["x"])], vec![NO_PARENT]); + let env = Environment::default(); + // Unlayered rule beats both layered rules. + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "#00f" + ); +} + +#[test] +fn important_beats_source_order() { + let sheet = load(&read_case("cascade/02-important.css")); + let sheet = sheet.sheet(); + let t = tree(vec![classed("x", &["x"])], vec![NO_PARENT]); + let env = Environment::default(); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "red" + ); +} + +#[test] +fn hex_colors_reach_computed_styles() { + let sheet = load(&read_case("colors/02-hex.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![classed("x", &["a"]), classed("y", &["f"])], + vec![NO_PARENT, NO_PARENT], + ); + let env = Environment::default(); + // 3-digit hex on `color`… + let a = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&a, "color"), "#fff"); + // …and on `border-color`. + let f = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&f, "border-color"), "#333"); +} + +#[test] +fn single_line_rules_reach_computed_styles() { + let sheet = load(&read_case("values/02-single-line.css")); + let sheet = sheet.sheet(); + let t = tree( + vec![classed("card", &["card", "x"]), classed("y", &["y"])], + vec![NO_PARENT, 0], + ); + let env = Environment::default(); + // Both declarations of the single-line rule survive. + let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "#fff"); + assert_eq!(get(&s, "padding"), "4px"); + // The nested rule opened and closed on the same line as its parent. + // (`blue` canonicalizes to `#00f`: equal-length ties go to hex.) + let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s, "color"), "#00f"); +} + +/// The wasm adapter drives compile → prepare → compute-tree → compile / +/// prepare-again inside one component instance; hosts must see live results +/// at every step. This pins the library-level property that makes that +/// sequence safe: the compiler and the stylesheet loader carry no state +/// across calls. (The component-level sequence is asserted in +/// `demo/smoke.mjs` — an apparent cross-call poison there was traced to a +/// host misreading the `option<>` discriminant over canonical-ABI padding, +/// not to engine state.) +#[test] +fn compile_and_prepare_survive_compute_tree() { + use bombshell_css_core::{decode_ir, encode_ir}; + + let env = Environment::default(); + let first = load(&read_case("colors/02-hex.css")); + let sheet = first.sheet(); + let t = tree( + vec![classed("x", &["a"]), classed("y", &["f"])], + vec![NO_PARENT, NO_PARENT], + ); + + bombshell_css_runtime::cascade::compute_tree(std::slice::from_ref(&sheet), &t, &env, &[]); + + // A later compile walks the same pipeline: identical source, identical IR. + let second = load(&read_case("colors/02-hex.css")); + assert_eq!(encode_ir(&first.ir), encode_ir(&second.ir)); + + // A later prepare of already-prepared IR bytes still decodes, loads, + // and computes. + let decoded = decode_ir(&encode_ir(&first.ir)).expect("re-encoded IR decodes"); + let reloaded = Stylesheet::from_ir(&decoded, 0); + let s = compute(std::slice::from_ref(&reloaded), &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "#fff"); + let f = compute(std::slice::from_ref(&reloaded), &t, 1, &env).unwrap(); + assert_eq!(get(&f, "border-color"), "#333"); +} diff --git a/experiments/css/crates/css-conformance/tests/conformance.rs b/experiments/css/crates/css-conformance/tests/conformance.rs new file mode 100644 index 0000000..8b181ce --- /dev/null +++ b/experiments/css/crates/css-conformance/tests/conformance.rs @@ -0,0 +1,215 @@ +//! Conformance corpus runner. +//! +//! Every `cases/**/*.css` must either compile cleanly or be marked with a +//! sibling `.error` file naming the expected diagnostic code. The compiled +//! IR must round-trip through the wire codec. Semantic behavior (matching, +//! cascade, conditions) is covered by `tests/behavior.rs`. + +use std::fs; +use std::path::{Path, PathBuf}; + +use bombshell_css_compiler::{CompileOptions, compile}; +use bombshell_css_core::{Origin, decode_ir, encode_ir}; + +fn case_dirs() -> Vec { + let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("cases"); + let mut dirs = vec![root.clone()]; + for entry in fs::read_dir(&root).expect("cases directory") { + let path = entry.expect("entry").path(); + if path.is_dir() { + dirs.push(path); + } + } + dirs +} + +fn case_files() -> Vec { + let mut files = Vec::new(); + for dir in case_dirs() { + for entry in fs::read_dir(&dir).expect("readable") { + let path = entry.expect("entry").path(); + if path.extension().and_then(|e| e.to_str()) == Some("css") { + files.push(path); + } + } + } + files.sort(); + files +} + +#[test] +fn corpus_compiles_and_round_trips() { + let files = case_files(); + assert!( + files.len() >= 12, + "expected a non-trivial corpus, found {files:?}" + ); + for path in &files { + let css = fs::read_to_string(path).expect("readable case"); + let error_marker = path.with_extension("error"); + let options = CompileOptions { + error_recovery: false, + ..CompileOptions::default() + }; + let output = compile(&css, Origin::Author, options); + + if error_marker.exists() { + let expected = fs::read_to_string(&error_marker) + .expect("readable marker") + .trim() + .to_string(); + assert!( + output.sheet.is_none(), + "case {} unexpectedly compiled; expected error {expected}", + path.display() + ); + assert!( + output.diagnostics.iter().any(|d| d.code == expected), + "case {} missing diagnostic code {expected}: {:?}", + path.display(), + output.diagnostics + ); + continue; + } + + assert!( + !output + .diagnostics + .iter() + .any(|d| d.severity == bombshell_css_core::Severity::Error), + "case {} produced errors: {:?}", + path.display(), + output.diagnostics + ); + let sheet = output.sheet.expect("sheet present"); + assert_eq!(sheet.version, bombshell_css_core::IR_VERSION); + let bytes = encode_ir(&sheet); + let decoded = decode_ir(&bytes).expect("round trip"); + assert_eq!(decoded, sheet, "case {} IR mismatch", path.display()); + assert!( + !sheet.rules.is_empty(), + "case {} produced no rules", + path.display() + ); + } +} + +#[test] +fn nesting_expands_against_the_spec() { + let css = read_case("nesting/01-basic.css"); + let sheet = compile_strict(&css); + let selectors: Vec<&str> = sheet.rules.iter().map(|r| r.selector.as_str()).collect(); + // Implicit nesting (`.body` without `&`) is a descendant relationship. + assert_eq!(selectors, vec![".card", ".card>.title", ".card .body"]); +} + +#[test] +fn deep_nesting_expands_every_ampersand() { + let css = read_case("nesting/02-deep-nesting.css"); + let sheet = compile_strict(&css); + let selectors: Vec<&str> = sheet.rules.iter().map(|r| r.selector.as_str()).collect(); + // Only the innermost rule carries declarations; ancestors without + // declarations collapse. `:not(&)` substitutes the fully expanded parent. + assert_eq!(selectors, vec![":not(.a .b.c)"]); +} + +#[test] +fn nested_conditions_keep_the_style_context() { + let css = read_case("nesting/03-nested-conditions.css"); + let sheet = compile_strict(&css); + assert_eq!(sheet.rules.len(), 3); + assert_eq!(sheet.rules[0].selector, ".panel"); + assert!(sheet.rules[0].media.is_empty()); + assert_eq!(sheet.rules[1].selector, ".panel .item"); + assert_eq!(sheet.rules[1].media.len(), 1); + assert_eq!(sheet.rules[2].selector, ".panel .item"); + assert_eq!(sheet.rules[2].containers.len(), 1); +} + +#[test] +fn level4_selectors_are_preserved() { + let css = read_case("selectors/01-level4.css"); + let sheet = compile_strict(&css); + let selectors: Vec<&str> = sheet.rules.iter().map(|r| r.selector.as_str()).collect(); + assert_eq!( + selectors, + vec![ + ":is(.a,.b)>.c", + ":where(#x,.y) .z", + ".item:not(.skip):not(.list>*)", + ".row:has(>.col)", + ".item:nth-child(odd of .tagged)", + ".item:nth-child(-n+3)", + "p:has(+h2)", + ] + ); +} + +#[test] +fn conditions_are_typed_in_the_ir() { + let css = read_case("conditions/01-media.css"); + let sheet = compile_strict(&css); + assert_eq!(sheet.rules.len(), 5); + // The interval form canonicalizes. + match &sheet.rules[0].media[0] { + bombshell_css_core::MediaCondition::Feature(feature) => { + assert_eq!(feature.name, "width"); + assert!(matches!( + feature.kind, + bombshell_css_core::FeatureKind::Interval { .. } + )); + } + other => panic!("unexpected media condition {other:?}"), + } + // The comma list becomes an OR branch. + match &sheet.rules[4].media[0] { + bombshell_css_core::MediaCondition::Or(branches) => assert_eq!(branches.len(), 2), + other => panic!("expected OR list, got {other:?}"), + } +} + +#[test] +fn layers_are_registered_in_first_appearance_order() { + let css = read_case("cascade/01-layers.css"); + let sheet = compile_strict(&css); + assert_eq!( + sheet.layer_order, + vec![ + vec!["a".to_string()], + vec!["b".to_string()], + vec!["b".to_string(), "c".to_string()], + ] + ); + assert_eq!( + sheet.rules[0].layer.as_deref(), + Some(["a".to_string()].as_slice()) + ); + assert_eq!( + sheet.rules[1].layer.as_deref(), + Some(["b".to_string(), "c".to_string()].as_slice()) + ); + assert_eq!(sheet.rules[2].layer, None); +} + +#[test] +fn important_is_tracked_per_declaration() { + let css = read_case("cascade/02-important.css"); + let sheet = compile_strict(&css); + assert!(sheet.rules[0].declarations[0].important); + assert!(!sheet.rules[1].declarations[0].important); +} + +fn read_case(rel: &str) -> String { + fs::read_to_string( + Path::new(env!("CARGO_MANIFEST_DIR")) + .join("cases") + .join(rel), + ) + .expect("case file") +} + +fn compile_strict(css: &str) -> bombshell_css_core::CompiledStylesheet { + let output = compile(css, Origin::Author, CompileOptions::default()); + assert!(output.diagnostics.is_empty(), "{:?}", output.diagnostics); + output.sheet.expect("sheet") +} diff --git a/experiments/css/crates/css-core/Cargo.toml b/experiments/css/crates/css-core/Cargo.toml new file mode 100644 index 0000000..2eac1f4 --- /dev/null +++ b/experiments/css/crates/css-core/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "bombshell-css-core" +version = "0.1.0" +description = "Shared contracts, typed CSS IR, and codec for the Bombshell CSS pipeline" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[features] +default = ["selectors"] +selectors = ["dep:parcel_selectors", "dep:cssparser"] + +[dependencies] +serde = { workspace = true } +postcard = { workspace = true } +cssparser = { workspace = true, optional = true } +parcel_selectors = { workspace = true, optional = true } diff --git a/experiments/css/crates/css-core/src/lib.rs b/experiments/css/crates/css-core/src/lib.rs new file mode 100644 index 0000000..ce4dadc --- /dev/null +++ b/experiments/css/crates/css-core/src/lib.rs @@ -0,0 +1,458 @@ +//! Shared contracts for the Bombshell CSS pipeline. +//! +//! The IR/DOM-snapshot half is intentionally dependency-light (`serde` + +//! `postcard`, both `no_std`-capable). The selector engine lives here too, +//! behind the default `selectors` feature (which pulls `cssparser` and +//! `parcel_selectors`): both the compiler and the runtime drive this one +//! parser so they can never disagree about what a valid selector is. + +#![cfg_attr(not(feature = "selectors"), no_std)] + +extern crate alloc; + +#[cfg(feature = "selectors")] +pub mod selectors; + +#[cfg(feature = "selectors")] +pub mod tokens; + +use alloc::{boxed::Box, string::String, vec::Vec}; +use serde::{Deserialize, Serialize}; + +/// Version of the compiled stylesheet IR. Bump when the wire format changes +/// incompatibly; runtimes must reject unknown versions. +pub const IR_VERSION: u16 = 2; + +/// The sentinel used in [`TreeSnapshot::parents`] for "no parent" (a root). +pub const NO_PARENT: u32 = u32::MAX; + +/// Cascade origin of a stylesheet, mirroring the CSS cascade order. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Origin { + /// Rules from the user agent stylesheet. + UserAgent, + /// Rules supplied by the user. + User, + /// Rules supplied by the document/author. + Author, +} + +/// Severity of a [`Diagnostic`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Severity { + /// A fatal or non-recoverable problem. + Error, + /// A recoverable problem worth reporting to authors. + Warning, +} + +/// A compile or runtime diagnostic. Messages are stable text; `code` is a +/// machine-readable identifier intended to grow over time. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Diagnostic { + pub severity: Severity, + pub code: String, + pub message: String, +} + +impl Diagnostic { + pub fn error(code: &str, message: impl Into) -> Self { + Diagnostic { + severity: Severity::Error, + code: code.into(), + message: message.into(), + } + } + + pub fn warning(code: &str, message: impl Into) -> Self { + Diagnostic { + severity: Severity::Warning, + code: code.into(), + message: message.into(), + } + } +} + +/// A flat node snapshot describing one element of the host tree. +/// +/// The host (tty/UI) ships the whole subtree it wants styled as a flat, +/// document-ordered node list plus a parent-index vector. This shape is +/// recursion-free (WIT-friendly) yet complete enough for structural +/// selectors, sibling combinators, `:has()` traversal, and container lookups. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +pub struct NodeSnapshot { + /// Element tag name, e.g. `box`, `text`. Case-sensitive. + pub tag: String, + /// Element id, if any. + pub id: Option, + /// Class names. + pub classes: Vec, + /// Attributes, in source order. Lookup is case-sensitive on both name and value. + pub attributes: Vec<(String, String)>, + /// Element has keyboard focus. + pub focus: bool, + /// Pointer is over the element. + pub hover: bool, + /// Element is being activated. + pub active: bool, + /// Layout width in CSS px, if known. Satisfies container/media size queries. + pub width: Option, + /// Layout height in CSS px, if known. + pub height: Option, +} + +/// A flat tree snapshot: nodes in document order plus each node's parent index. +/// +/// Nodes are in document order and siblings are contiguous runs that share a +/// parent. [`NO_PARENT`] marks roots. +#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] +pub struct TreeSnapshot { + pub nodes: Vec, + /// `parents[i]` is the index of the parent of node `i`, or [`NO_PARENT`]. + pub parents: Vec, +} + +impl TreeSnapshot { + pub fn parent_of(&self, index: usize) -> Option { + self.parents + .get(index) + .and_then(|p| (*p != NO_PARENT).then_some(*p as usize)) + } + + pub fn children_of(&self, index: usize) -> impl Iterator + '_ { + self.parents + .iter() + .enumerate() + .filter(move |&(_i, p)| *p == index as u32) + .map(|(i, _)| i) + } +} + +/// Color scheme preference for `prefers-color-scheme`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum ColorScheme { + Light, + Dark, +} + +/// Rendering environment supplied with every `compute` call. Unknown +/// dimensions are `None`; queries involving unknown dimensions do not match. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Environment { + pub viewport_width: Option, + pub viewport_height: Option, + pub color_scheme: ColorScheme, + /// `true` when the user prefers reduced motion. + pub reduced_motion: bool, + /// Device pixel ratio. + pub resolution: f32, +} + +impl Default for Environment { + fn default() -> Self { + Environment { + viewport_width: None, + viewport_height: None, + color_scheme: ColorScheme::Light, + reduced_motion: false, + resolution: 1.0, + } + } +} + +// --------------------------------------------------------------------------- +// Compiled stylesheet IR +// --------------------------------------------------------------------------- + +/// A compiled stylesheet: flattened, nesting-expanded rules with typed +/// condition trees, ready for linear-memory transport. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CompiledStylesheet { + /// [`IR_VERSION`] of the emitting compiler. + pub version: u16, + /// Origin this stylesheet was compiled for. + pub origin: Origin, + /// Cascade rules in source order. + pub rules: Vec, + /// Declared `@layer` names in first-appearance order. Nesting is a path. + pub layer_order: Vec>, + /// Preserved `@keyframes` blocks (not yet evaluated at runtime). + pub keyframes: Vec, + /// Preserved opaque at-rules (`@font-face`, `@property`, `@scope`, ...). + pub opaque: Vec, + /// Warnings collected during compilation. + pub warnings: Vec, +} + +/// One flattened, nesting-expanded cascade rule. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CompiledRule { + /// Canonical serialized selector list (comma-separated). + pub selector: String, + /// Max specificity across the selector list, parcel_selectors-packed + /// `(ids << 20) | (classes << 10) | elements`. + pub specificity: u32, + /// Layer path this rule lives in, if any. + pub layer: Option>, + /// Media conditions. All must match. + pub media: Vec, + /// Container conditions. All must match. + pub containers: Vec, + /// `@supports` conditions. All must match. + pub supports: Vec, + /// Declarations, canonicalized per rule (each property at most once). + pub declarations: Vec, + /// Source order, unique within the originating stylesheet. + pub order: u32, +} + +/// A single declaration with its canonical value text. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct Declaration { + /// Canonical property name (kebab-case, or `--custom`). + pub name: String, + /// Canonical serialized value. + pub value: String, + /// Whether the declaration carried `!important`. + pub important: bool, +} + +/// A preserved `@keyframes` block. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct KeyframesRule { + pub name: String, + /// Canonical serialized body (everything inside the block). + pub body: String, +} + +/// A preserved opaque at-rule. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub struct OpaqueRule { + /// Canonical serialized at-rule text. + pub text: String, +} + +/// Comparison operator for range features. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum Compare { + Equal, + GreaterThan, + GreaterThanEqual, + LessThan, + LessThanEqual, +} + +/// A value appearing in a media/container feature. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum FeatureValue { + /// Length already resolved to CSS px (absolute units). + Px(f32), + /// Length in a relative unit that needs a reference at evaluation time. + RelativeLength { + value: f32, + unit: String, + }, + Number(f32), + Integer(i32), + Boolean(bool), + /// Resolution normalized to dppx. + Dppx(f32), + Ratio(f32, f32), + Ident(String), + /// Unrepresentable value; conditions using it never match. + Unknown, +} + +/// The evaluated shape of a media or container size feature. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum FeatureKind { + /// `(width: 400px)` / `(min-width: 400px)`. + Plain { value: FeatureValue }, + /// `(hover)` — presence test. + Boolean, + /// `(width > 400px)`. + Range { cmp: Compare, value: FeatureValue }, + /// `(400px < width < 800px)`. + Interval { + start: FeatureValue, + start_cmp: Compare, + end: FeatureValue, + end_cmp: Compare, + }, +} + +/// A media or container size feature: canonical name plus its shape. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct Feature { + /// Canonical feature name (`min-width`, `width`, `aspect-ratio`, ...). + pub name: String, + pub kind: FeatureKind, +} + +/// Boolean algebra over media features. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum MediaCondition { + Feature(Feature), + Not(Box), + And(Vec), + Or(Vec), + /// Unparsed/unknown condition; never matches. + Unknown, +} + +/// A `@container` query: optional container name plus its condition. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ContainerQuery { + /// Required container name, if the query was named. + pub name: Option, + pub condition: ContainerCondition, +} + +/// Boolean algebra over container conditions. +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum ContainerCondition { + /// Size feature (`width`, `inline-size`, `aspect-ratio`, ...). + Size(Feature), + /// Style query (`style(color: red)`), preserved as canonical text. + Style { + /// Canonical declaration text, e.g. `color: red`, or a bare property + /// name to test for non-initial value. + text: String, + }, + Not(Box), + And(Vec), + Or(Vec), + /// Scroll-state or unknown conditions; never match (for now). + Unknown, +} + +/// `@supports` condition with leaves pre-evaluated at compile time. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum SupportsCondition { + /// Declaration support: `supported` was determined by the compiler. + Declaration { + name: String, + value: String, + supported: bool, + }, + /// `selector(...)` support, validated at compile time. + Selector { + text: String, + supported: bool, + }, + Not(Box), + And(Vec), + Or(Vec), + Unknown, +} + +// --------------------------------------------------------------------------- +// Codec +// --------------------------------------------------------------------------- + +/// IR decode failure. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum IrError { + /// Bytes are not valid postcard IR. + Malformed, + /// IR decoded but carries an unsupported [`IR_VERSION`]. + UnsupportedVersion(u16), +} + +impl core::fmt::Display for IrError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + IrError::Malformed => f.write_str("malformed stylesheet IR"), + IrError::UnsupportedVersion(v) => { + write!(f, "unsupported IR version {v} (expected {IR_VERSION})") + } + } + } +} + +/// Encode a compiled stylesheet for linear-memory transport. +pub fn encode_ir(sheet: &CompiledStylesheet) -> Vec { + postcard::to_allocvec(sheet).unwrap_or_default() +} + +/// Decode and validate a compiled stylesheet. +pub fn decode_ir(bytes: &[u8]) -> Result { + let sheet: CompiledStylesheet = postcard::from_bytes(bytes).map_err(|_| IrError::Malformed)?; + if sheet.version != IR_VERSION { + return Err(IrError::UnsupportedVersion(sheet.version)); + } + Ok(sheet) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec; + + fn sample() -> CompiledStylesheet { + CompiledStylesheet { + version: IR_VERSION, + origin: Origin::Author, + rules: vec![CompiledRule { + selector: ".card > .title".into(), + specificity: 0x200020, + layer: Some(vec!["base".into(), "typography".into()]), + media: vec![MediaCondition::Feature(Feature { + name: "min-width".into(), + kind: FeatureKind::Plain { + value: FeatureValue::Px(384.0), + }, + })], + containers: vec![ContainerQuery { + name: Some("panel".into()), + condition: ContainerCondition::Size(Feature { + name: "inline-size".into(), + kind: FeatureKind::Range { + cmp: Compare::GreaterThan, + value: FeatureValue::Px(720.0), + }, + }), + }], + supports: vec![SupportsCondition::Declaration { + name: "color".into(), + value: "oklch(70% 0.2 40)".into(), + supported: true, + }], + declarations: vec![Declaration { + name: "color".into(), + value: "oklch(70% 0.2 40)".into(), + important: false, + }], + order: 3, + }], + layer_order: vec![ + vec!["base".into()], + vec!["base".into(), "typography".into()], + ], + keyframes: vec![KeyframesRule { + name: "pulse".into(), + body: "0% {opacity:1} 100% {opacity:0}".into(), + }], + opaque: vec![OpaqueRule { + text: "@font-face{font-family:x}".into(), + }], + warnings: vec![Diagnostic::warning("test", "sample warning")], + } + } + + #[test] + fn round_trips() { + let sheet = sample(); + let bytes = encode_ir(&sheet); + let decoded = decode_ir(&bytes).unwrap(); + assert_eq!(decoded, sheet); + } + + #[test] + fn rejects_wrong_version() { + let mut sheet = sample(); + sheet.version = 99; + let bytes = encode_ir(&sheet); + assert_eq!(decode_ir(&bytes), Err(IrError::UnsupportedVersion(99))); + } +} diff --git a/experiments/css/crates/css-core/src/selectors.rs b/experiments/css/crates/css-core/src/selectors.rs new file mode 100644 index 0000000..554e047 --- /dev/null +++ b/experiments/css/crates/css-core/src/selectors.rs @@ -0,0 +1,526 @@ +//! The selector engine: a `parcel_selectors` `SelectorImpl` for the Bombshell +//! DOM snapshot model, the parser that accepts canonical selector text, and +//! the desugaring pass that rewrites components the matcher cannot execute +//! (`Nesting`, `Has`, `NthOf`, shadow-DOM parts) into interception points +//! owned by the runtime. +//! +//! Both the compiler (validation + specificity) and the runtime (matching) +//! drive this one parser, so the two sides can never disagree about what a +//! valid selector is. + +#![cfg(feature = "selectors")] + +use alloc::boxed::Box; +use alloc::vec::Vec; + +use cssparser::{ + CowRcStr, ParseError, Parser as CssParser, ParserInput, ToCss, match_ignore_ascii_case, +}; +use parcel_selectors::context::{MatchingContext, MatchingMode, QuirksMode}; +use parcel_selectors::matching::matches_selector_list; +use parcel_selectors::parser::{ + Component, NestingRequirement, NthOfSelectorData, ParseErrorRecovery, + Parser as SelectorParserTrait, SelectorImpl, SelectorParseErrorKind, +}; +use parcel_selectors::{NthIndexCache, SelectorList as ParcelSelectorList}; + +use alloc::borrow::Cow; +use core::fmt; + +/// `SelectorImpl` for the pipeline: identifiers are ref-counted strings. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Selectors; + +pub type SelectorList<'i> = ParcelSelectorList<'i, Selectors>; +pub type Selector<'i> = parcel_selectors::parser::Selector<'i, Selectors>; + +/// An interned-ish identifier backed by `CowRcStr`. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Ident<'i>(pub CowRcStr<'i>); + +impl<'i> From> for Ident<'i> { + fn from(value: CowRcStr<'i>) -> Self { + Ident(value) + } +} + +impl<'i> From> for Ident<'i> { + fn from(value: Cow<'i, str>) -> Self { + Ident(CowRcStr::from(value)) + } +} + +impl AsRef for Ident<'_> { + fn as_ref(&self) -> &str { + &self.0 + } +} + +impl<'i> core::borrow::Borrow for Ident<'i> { + fn borrow(&self) -> &str { + &self.0 + } +} + +impl Default for Ident<'_> { + fn default() -> Self { + Ident(CowRcStr::from("")) + } +} + +impl ToCss for Ident<'_> { + fn to_css(&self, dest: &mut W) -> fmt::Result { + dest.write_str(&self.0) + } +} + +/// Non-tree-structural pseudo-classes understood by the runtime matcher. +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum PseudoClass<'i> { + Hover, + Active, + Focus, + FocusVisible, + FocusWithin, + Disabled, + Enabled, + Checked, + Required, + Optional, + ReadOnly, + ReadWrite, + PlaceholderShown, + Lang(Vec>), + Dir(CowRcStr<'i>), + /// Desugared `:has(...)`; evaluated by traversal in the runtime. + Has(Box<[Selector<'i>]>), + /// Desugared `:nth-child(.. of S)`; evaluated by counting. + NthOf(NthOfSelectorData<'i, Selectors>), + /// Desugared components that can never match in the terminal model + /// (`&` remnants, `:host`, `::slotted`, `::part`, unknowns). + NeverMatches, +} + +impl fmt::Debug for PseudoClass<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + PseudoClass::Hover => ":hover", + PseudoClass::Active => ":active", + PseudoClass::Focus => ":focus", + PseudoClass::FocusVisible => ":focus-visible", + PseudoClass::FocusWithin => ":focus-within", + PseudoClass::Disabled => ":disabled", + PseudoClass::Enabled => ":enabled", + PseudoClass::Checked => ":checked", + PseudoClass::Required => ":required", + PseudoClass::Optional => ":optional", + PseudoClass::ReadOnly => ":read-only", + PseudoClass::ReadWrite => ":read-write", + PseudoClass::PlaceholderShown => ":placeholder-shown", + PseudoClass::Lang(_) => ":lang()", + PseudoClass::Dir(_) => ":dir()", + PseudoClass::Has(_) => ":has()", + PseudoClass::NthOf(_) => ":nth-child(of)", + PseudoClass::NeverMatches => ":never", + }) + } +} + +impl<'i> parcel_selectors::parser::NonTSPseudoClass<'i> for PseudoClass<'i> { + type Impl = Selectors; + + fn is_active_or_hover(&self) -> bool { + matches!(self, PseudoClass::Hover | PseudoClass::Active) + } + + fn is_user_action_state(&self) -> bool { + matches!( + self, + PseudoClass::Hover + | PseudoClass::Active + | PseudoClass::Focus + | PseudoClass::FocusVisible + | PseudoClass::FocusWithin + ) + } +} + +impl ToCss for PseudoClass<'_> { + fn to_css(&self, dest: &mut W) -> fmt::Result { + let name = match self { + PseudoClass::Hover => "hover", + PseudoClass::Active => "active", + PseudoClass::Focus => "focus", + PseudoClass::FocusVisible => "focus-visible", + PseudoClass::FocusWithin => "focus-within", + PseudoClass::Disabled => "disabled", + PseudoClass::Enabled => "enabled", + PseudoClass::Checked => "checked", + PseudoClass::Required => "required", + PseudoClass::Optional => "optional", + PseudoClass::ReadOnly => "read-only", + PseudoClass::ReadWrite => "read-write", + PseudoClass::PlaceholderShown => "placeholder-shown", + PseudoClass::Lang(_) => "lang", + PseudoClass::Dir(_) => "dir", + PseudoClass::Has(_) => "has", + PseudoClass::NthOf(_) => "nth-child", + PseudoClass::NeverMatches => return Ok(()), + }; + dest.write_str(name) + } +} + +/// Pseudo-elements are parsed for syntax compatibility but never match: the +/// terminal engine has no generated content yet. +#[derive(Clone, PartialEq, Eq, Hash)] +pub enum PseudoElement { + Before, + After, + Marker, + Selection, + FirstLine, + FirstLetter, + Placeholder, + Backdrop, + Unknown, +} + +impl fmt::Debug for PseudoElement { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + PseudoElement::Before => "::before", + PseudoElement::After => "::after", + PseudoElement::Marker => "::marker", + PseudoElement::Selection => "::selection", + PseudoElement::FirstLine => "::first-line", + PseudoElement::FirstLetter => "::first-letter", + PseudoElement::Placeholder => "::placeholder", + PseudoElement::Backdrop => "::backdrop", + PseudoElement::Unknown => "::unknown", + }) + } +} + +impl parcel_selectors::parser::PseudoElement<'_> for PseudoElement { + type Impl = Selectors; +} + +impl ToCss for PseudoElement { + fn to_css(&self, dest: &mut W) -> fmt::Result { + let name = match self { + PseudoElement::Before => "before", + PseudoElement::After => "after", + PseudoElement::Marker => "marker", + PseudoElement::Selection => "selection", + PseudoElement::FirstLine => "first-line", + PseudoElement::FirstLetter => "first-letter", + PseudoElement::Placeholder => "placeholder", + PseudoElement::Backdrop => "backdrop", + PseudoElement::Unknown => return Ok(()), + }; + dest.write_str(name) + } +} + +/// Vendor prefixes never reach the runtime (compiler-canonical selectors, +/// `parse_any_prefix` returns `None`), so an uninhabited type satisfies the +/// `SelectorImpl` bounds. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum VendorPrefix {} + +impl ToCss for VendorPrefix { + fn to_css(&self, _dest: &mut W) -> fmt::Result { + match *self {} + } +} + +impl<'i> SelectorImpl<'i> for Selectors { + type ExtraMatchingData = (); + type AttrValue = Ident<'i>; + type Identifier = Ident<'i>; + type LocalName = Ident<'i>; + type NamespaceUrl = Ident<'i>; + type NamespacePrefix = Ident<'i>; + type BorrowedNamespaceUrl = str; + type BorrowedLocalName = str; + + type NonTSPseudoClass = PseudoClass<'i>; + type PseudoElement = PseudoElement; + type VendorPrefix = VendorPrefix; +} + +/// The selector parser for canonical compiler output. +pub struct SelectorParser; + +impl<'i> SelectorParserTrait<'i> for SelectorParser { + type Impl = Selectors; + type Error = SelectorParseErrorKind<'i>; + + fn parse_is_and_where(&self) -> bool { + true + } + + fn parse_non_ts_pseudo_class( + &self, + _location: cssparser::SourceLocation, + name: CowRcStr<'i>, + ) -> Result, ParseError<'i, Self::Error>> { + match_ignore_ascii_case! { &name, + "hover" => Ok(PseudoClass::Hover), + "active" => Ok(PseudoClass::Active), + "focus" => Ok(PseudoClass::Focus), + "focus-visible" => Ok(PseudoClass::FocusVisible), + "focus-within" => Ok(PseudoClass::FocusWithin), + "disabled" => Ok(PseudoClass::Disabled), + "enabled" => Ok(PseudoClass::Enabled), + "checked" => Ok(PseudoClass::Checked), + "required" => Ok(PseudoClass::Required), + "optional" => Ok(PseudoClass::Optional), + "read-only" => Ok(PseudoClass::ReadOnly), + "read-write" => Ok(PseudoClass::ReadWrite), + "placeholder-shown" => Ok(PseudoClass::PlaceholderShown), + _ => Err(_location.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name))), + } + } + + fn parse_non_ts_functional_pseudo_class<'t>( + &self, + name: CowRcStr<'i>, + arguments: &mut CssParser<'i, 't>, + ) -> Result, ParseError<'i, Self::Error>> { + match_ignore_ascii_case! { &name, + "lang" => { + let mut languages = Vec::new(); + loop { + if arguments + .try_parse(|i| i.expect_delim('*')) + .is_ok() + { + languages.push(CowRcStr::from("*")); + } else { + languages.push(arguments.expect_ident()?.clone()); + } + if arguments.try_parse(|i| i.expect_comma()).is_err() { + break; + } + } + Ok(PseudoClass::Lang(languages)) + }, + "dir" => { + let ident = arguments.expect_ident()?.clone(); + Ok(PseudoClass::Dir(ident)) + }, + _ => Err(arguments.new_custom_error(SelectorParseErrorKind::UnsupportedPseudoClass(name))), + } + } + + fn parse_pseudo_element( + &self, + _location: cssparser::SourceLocation, + name: CowRcStr<'i>, + ) -> Result> { + match_ignore_ascii_case! { &name, + "before" => Ok(PseudoElement::Before), + "after" => Ok(PseudoElement::After), + "marker" => Ok(PseudoElement::Marker), + "selection" => Ok(PseudoElement::Selection), + "first-line" => Ok(PseudoElement::FirstLine), + "first-letter" => Ok(PseudoElement::FirstLetter), + "placeholder" => Ok(PseudoElement::Placeholder), + "backdrop" => Ok(PseudoElement::Backdrop), + _ => Ok(PseudoElement::Unknown), + } + } +} + +/// Per-element matching state: the `NthIndexCache` is element-scoped, so it +/// is built once per cascade step and shared across every rule matched +/// against that element. +pub struct MatchContext { + cache: NthIndexCache, +} + +impl Default for MatchContext { + fn default() -> Self { + Self::new() + } +} + +impl MatchContext { + pub fn new() -> Self { + MatchContext { + cache: NthIndexCache::default(), + } + } + + /// Match a parsed selector list against one element. + pub fn matches<'i, E>( + &mut self, + element: &E, + list: &SelectorList<'i>, + ) -> bool + where + E: parcel_selectors::Element<'i, Impl = Selectors>, + { + let mut context = MatchingContext::new( + MatchingMode::Normal, + None, + Some(&mut self.cache), + QuirksMode::NoQuirks, + ); + matches_selector_list(list, element, &mut context) + } +} + +/// Whether the selector can never match in the terminal model: it carries a +/// pseudo-element (no generated content yet) or a desugared `NeverMatches` +/// component (`&` remnants, `:host`, `::slotted`, `::part`) at the top +/// level. Components nested inside functional pseudo-classes only disable +/// that inner branch, so they do not disqualify the selector. +pub fn selector_never_matches(selector: &Selector) -> bool { + selector.has_pseudo_element() + || selector + .iter_raw_match_order() + .any(|c| matches!(c, Component::NonTSPseudoClass(PseudoClass::NeverMatches))) +} + +/// The "index keys" of a selector: tag/class/id names such that an element +/// missing all of them cannot match. Keys come from the rightmost compound +/// (positive requirements only — `:not()` contributes nothing, `:has()` +/// constrains descendants, not the element). An empty result means the +/// selector must always be tried. +/// +/// `:is()`/`:where()` and `:nth-child(.. of S)` contribute the union of +/// their inner selectors' rightmost keys: the element has to satisfy at +/// least one inner selector, so it must carry one of their keys. +pub fn selector_keys<'i>(selector: &Selector<'i>) -> Vec { + let mut keys = Vec::new(); + collect_keys(selector, &mut keys); + keys +} + +fn collect_keys<'i>(selector: &Selector<'i>, keys: &mut Vec) { + // Raw match order runs right-to-left (matching starts at the rightmost + // compound), so the keys come from the head of the iteration. + for component in selector.iter_raw_match_order() { + match component { + Component::Combinator(..) => break, + Component::Class(name) | Component::ID(name) => { + keys.push(String::from(name.as_ref())) + } + Component::LocalName(name) => { + // Both spellings: the matcher may consult either. + keys.push(String::from(name.name.as_ref())); + keys.push(String::from(name.lower_name.as_ref())); + } + Component::Is(list) | Component::Where(list) | Component::Any(_, list) => { + for inner in &**list { + collect_keys(inner, keys); + } + } + Component::NthOf(data) => { + for inner in data.selectors() { + collect_keys(inner, keys); + } + } + // `:not()` and `:has()` add no positive requirements on the + // element itself; attributes and pseudo-classes add none either. + _ => {} + } + } +} + +/// Validate a (nesting-expanded) selector list and return its specificity: +/// the max across entries, parcel-packed `(ids << 20) | (classes << 10) | +/// elements`. Specificity comes from the same engine that matches at runtime +/// (`:is()`/`:not()` take the max of their arguments; `:where()`/`:has()` +/// contribute nothing). +pub fn validate_and_specificity(text: &str) -> Result { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + let list = SelectorList::parse( + &SelectorParser, + &mut parser, + ParseErrorRecovery::DiscardList, + NestingRequirement::None, + ) + .map_err(|_| ())?; + Ok(list.0.iter().map(|s| s.specificity()).max().unwrap_or(0)) +} + +/// Parse canonical selector text produced by the compiler. +/// +/// The returned selectors borrow from `text`; keep the text alive for as +/// long as the list (the IR owns it, so loaded stylesheets hold it anyway). +pub fn parse_selector_list<'i>( + text: &'i str, +) -> Result, ParseError<'i, SelectorParseErrorKind<'i>>> { + let mut input = ParserInput::new(text); + let mut parser = CssParser::new(&mut input); + let mut list = SelectorList::parse( + &SelectorParser, + &mut parser, + ParseErrorRecovery::IgnoreInvalidSelector, + NestingRequirement::None, + )?; + desugar_list(&mut list); + Ok(list) +} + +/// Rewrite components the upstream matcher cannot execute: +/// +/// - `Has(...)` → `PseudoClass::Has(...)`, evaluated by traversal +/// - `NthOf(...)` → `PseudoClass::NthOf(...)`, evaluated by counting +/// - `Nesting` (`&`), `Host`, `Slotted`, `Part` → `NeverMatches` +/// +/// Desugaring preserves the specificity parcel computed at parse time +/// (`:has()` weighs 0 here where the spec takes the max of its arguments). +pub fn desugar_list(list: &mut SelectorList) { + for selector in &mut list.0 { + desugar_selector(selector); + } +} + +fn desugar_selector(selector: &mut Selector) { + for component in selector.iter_mut_raw_match_order() { + desugar_component(component); + } +} + +fn desugar_component(component: &mut Component<'_, Selectors>) { + match component { + Component::Has(_) => { + let dummy = Component::ExplicitUniversalType; + let old = core::mem::replace(component, dummy); + if let Component::Has(selectors) = old { + let mut selectors = selectors.into_vec(); + for selector in &mut selectors { + desugar_selector(selector); + } + *component = + Component::NonTSPseudoClass(PseudoClass::Has(selectors.into_boxed_slice())); + } + } + Component::NthOf(_) => { + let dummy = Component::ExplicitUniversalType; + let old = core::mem::replace(component, dummy); + if let Component::NthOf(data) = old { + *component = Component::NonTSPseudoClass(PseudoClass::NthOf(data)); + } + } + Component::Nesting | Component::Host(_) | Component::Slotted(_) | Component::Part(_) => { + *component = Component::NonTSPseudoClass(PseudoClass::NeverMatches); + } + Component::Is(selectors) + | Component::Where(selectors) + | Component::Negation(selectors) + | Component::Any(_, selectors) => { + for selector in selectors.iter_mut() { + desugar_selector(selector); + } + } + _ => {} + } +} + diff --git a/experiments/css/crates/css-core/src/tokens.rs b/experiments/css/crates/css-core/src/tokens.rs new file mode 100644 index 0000000..c034d52 --- /dev/null +++ b/experiments/css/crates/css-core/src/tokens.rs @@ -0,0 +1,172 @@ +//! Canonical CSS token serialization — the one writer for re-emitting +//! tokenized value/selector text. +//! +//! Both the compiler (value canonicalization, nth canonicalization, +//! condition text) and the runtime (`var()` substitution) rebuild canonical +//! CSS from `cssparser` token streams. This module is that single writer: +//! block tokens serialize as their opening delimiter and callers descend +//! into the nested block themselves; numbers take the pipeline's canonical +//! number form (shortest round-trip f32, no redundant zeros or signs). + +#![cfg(feature = "selectors")] + +use core::fmt::Write as _; + +use cssparser::{CssStringWriter, Token, serialize_identifier, serialize_name, serialize_string}; + +/// CSS url() body escaping: control characters and spaces hex-escape, the +/// delimiter set backslash-escapes, everything else is literal. +fn serialize_unquoted_url(value: &str, out: &mut String) { + let mut chunk_start = 0; + for (i, byte) in value.bytes().enumerate() { + let hex = matches!(byte, b'\0'..=b' ' | b'\x7F'); + let special = matches!(byte, b'(' | b')' | b'"' | b'\'' | b'\\'); + if !hex && !special { + continue; + } + out.push_str(&value[chunk_start..i]); + if hex { + hex_escape(byte, out); + } else { + out.push('\\'); + out.push(byte as char); + } + chunk_start = i + 1; + } + out.push_str(&value[chunk_start..]); +} + +fn hex_escape(byte: u8, out: &mut String) { + const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef"; + if byte > 0x0F { + out.push('\\'); + out.push(HEX_DIGITS[(byte >> 4) as usize] as char); + out.push(HEX_DIGITS[(byte & 0x0F) as usize] as char); + } else { + out.push('\\'); + out.push(HEX_DIGITS[byte as usize] as char); + } + out.push(' '); +} + +/// Canonical CSS text of one token. Block tokens (`Function`, bracket +/// blocks) write only their opening delimiter; the caller consumes the +/// nested block and writes the closer. +pub fn write_token(out: &mut String, token: &Token<'_>) { + match *token { + Token::Ident(ref value) => { + let _ = serialize_identifier(value, out); + } + Token::AtKeyword(ref value) => { + out.push('@'); + let _ = serialize_identifier(value, out); + } + Token::Hash(ref value) => { + out.push('#'); + let _ = serialize_name(value, out); + } + Token::IDHash(ref value) => { + out.push('#'); + let _ = serialize_identifier(value, out); + } + Token::QuotedString(ref value) => { + let _ = serialize_string(value, out); + } + Token::UnquotedUrl(ref value) => { + out.push_str("url("); + serialize_unquoted_url(value, out); + out.push(')'); + } + Token::Delim(value) => out.push(value), + + Token::Number { value, has_sign, .. } => write_number(out, value, has_sign), + Token::Percentage { unit_value, .. } => { + write_number(out, unit_value * 100.0, false); + out.push('%'); + } + Token::Dimension { value, ref unit, has_sign, .. } => { + write_number(out, value, has_sign); + // Disambiguate with scientific notation: a unit starting with + // `e`/`E` would read as an exponent. + let unit = &**unit; + if unit == "e" || unit == "E" || unit.starts_with("e-") || unit.starts_with("E-") { + out.push_str("\\65 "); + let _ = serialize_name(&unit[1..], out); + } else { + let _ = serialize_identifier(unit, out); + } + } + + Token::WhiteSpace(content) => out.push_str(content), + Token::Comment(content) => { + out.push_str("/*"); + out.push_str(content); + out.push_str("*/"); + } + Token::Colon => out.push(':'), + Token::Semicolon => out.push(';'), + Token::Comma => out.push(','), + Token::IncludeMatch => out.push_str("~="), + Token::DashMatch => out.push_str("|="), + Token::PrefixMatch => out.push_str("^="), + Token::SuffixMatch => out.push_str("$="), + Token::SubstringMatch => out.push_str("*="), + Token::CDO => out.push_str(""), + + Token::Function(ref name) => { + let _ = serialize_identifier(name, out); + out.push('('); + } + Token::ParenthesisBlock => out.push('('), + Token::SquareBracketBlock => out.push('['), + Token::CurlyBracketBlock => out.push('{'), + + Token::BadUrl(ref contents) => { + out.push_str("url("); + out.push_str(contents); + out.push(')'); + } + Token::BadString(ref value) => { + // The token ends just before its newline, with no closing quote; + // the content is still escaped like a string body. + out.push('"'); + let mut escaped = String::new(); + let _ = CssStringWriter::new(&mut escaped).write_str(value); + out.push_str(&escaped); + } + Token::CloseParenthesis => out.push(')'), + Token::CloseSquareBracket => out.push(']'), + Token::CloseCurlyBracket => out.push('}'), + } +} + +/// Canonical number text: shortest round-trip f32, `-0` normalized, leading +/// zeros dropped (`.2`), trailing zeros dropped (`2.50` → `2.5`), integers +/// left integer. +pub fn write_number(out: &mut String, value: f32, _has_sign: bool) { + // Shortest round-trip f32 text (core's float Display). dtoa's f32 + // output diverges from this in the 7th+ significant digit, and the + // canonical form is pinned by conformance — keep the core formatter. + let text = format!("{value}"); + if !text.contains('.') { + out.push_str(&text); + return; + } + + let text = text.trim_end_matches('0'); + let text = if text.ends_with('.') { &text[..text.len() - 1] } else { text }; + match text.strip_prefix("-0.") { + Some(stripped) => { + out.push_str("-."); + out.push_str(stripped); + } + None => match text.strip_prefix("0.") { + Some(stripped) => { + out.push('.'); + out.push_str(stripped); + } + None => out.push_str(text), + }, + } +} diff --git a/experiments/css/crates/css-runtime/Cargo.toml b/experiments/css/crates/css-runtime/Cargo.toml new file mode 100644 index 0000000..193cf92 --- /dev/null +++ b/experiments/css/crates/css-runtime/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bombshell-css-runtime" +version = "0.1.0" +description = "Selector matching and cascade runtime for the Bombshell CSS pipeline" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +bombshell-css-core = { workspace = true } +parcel_selectors = { workspace = true } +rustc-hash = { workspace = true } +cssparser = { workspace = true } + +[dev-dependencies] +bombshell-css-compiler = { workspace = true } diff --git a/experiments/css/crates/css-runtime/src/cascade.rs b/experiments/css/crates/css-runtime/src/cascade.rs new file mode 100644 index 0000000..d159c03 --- /dev/null +++ b/experiments/css/crates/css-runtime/src/cascade.rs @@ -0,0 +1,529 @@ +//! Cascade resolution: origins and importance, `@layer` ordering, +//! specificity, source order, then inheritance and custom-property +//! resolution. + +use std::borrow::Cow; + +use crate::conditions::{ + ContainerAncestor, ContainerType, eval_container_list, eval_media_list, eval_supports_list, +}; +use crate::selectors::parse_selector_list; +use crate::values::resolve_vars; +use bombshell_css_core::{CompiledStylesheet, ContainerQuery, Declaration, Origin, TreeSnapshot}; +use rustc_hash::FxHashMap as HashMap; + +/// Property map for one element: keys are canonical IR property names; +/// values borrow the IR until a `var()` substitution or inheritance +/// materializes something new. +pub type StyleMap<'ir> = HashMap<&'ir str, Cow<'ir, str>>; + +/// Properties that inherit by default (CSS-wide custom properties also +/// inherit unconditionally). +pub const INHERITED_PROPERTIES: &[&str] = &[ + "color", + "opacity", + "font", + "font-family", + "font-size", + "font-weight", + "font-style", + "font-stretch", + "font-variation-settings", + "font-feature-settings", + "line-height", + "letter-spacing", + "word-spacing", + "white-space", + "text-align", + "text-align-last", + "text-transform", + "text-indent", + "text-underline-offset", + "text-decoration-thickness", + "text-emphasis-color", + "text-emphasis-style", + "visibility", + "cursor", + "direction", + "writing-mode", + "tab-size", + "user-select", + "list-style-type", + "list-style-position", + "border-collapse", + "border-spacing", + "caption-side", + "empty-cells", + "quotes", + "orphans", + "widows", + "caret-color", +]; + +/// A stylesheet loaded for cascade: parsed selectors over the typed IR. +/// +/// Borrows from the [`CompiledStylesheet`] it was loaded from; the IR owns +/// all selector and declaration text, so loading is allocation-free apart +/// from parsing. +#[derive(Clone)] +pub struct Stylesheet<'ir> { + pub origin: Origin, + pub sheet_index: usize, + /// Rules in IR order; rules whose selectors failed to parse are skipped + /// (their selector text shows up in [`Self::unmatched_selectors`]). + pub rules: Vec>, + pub layer_order: &'ir [Vec], + /// Candidate pre-filter: rule indices keyed by the tag/class/id names + /// their selectors' rightmost compounds require. Matching only consults + /// the element's own keys plus the always-checked bucket, which keeps + /// per-frame work proportional to relevant rules instead of all rules. + index: RuleIndex, + parse_warnings: Vec, +} + +#[derive(Clone, Default)] +struct RuleIndex { + /// Rule indices per index key; a key may name a class, a tag, or an id — + /// collisions only add false-positive candidates, never miss one. + /// Owned keys: the index is built once per stylesheet load. + keys: HashMap>, + /// Rules whose selectors carry no index key and are always checked. + universal: Vec, +} + +impl RuleIndex { + fn build<'ir>(rules: &[CascadeRule<'ir>]) -> RuleIndex { + let mut index = RuleIndex::default(); + for (rule_index, rule) in rules.iter().enumerate() { + let rule_index = rule_index as u32; + let mut keys: Vec = Vec::new(); + let mut always = false; + let mut indexable = false; + for selector in &rule.parsed.0 { + // Selectors that can never match are not indexed at all: + // checking them per element would be pure waste. + if bombshell_css_core::selectors::selector_never_matches(selector) { + continue; + } + indexable = true; + let selector_keys = bombshell_css_core::selectors::selector_keys(selector); + if selector_keys.is_empty() { + // One un-keyed selector in the list means any element + // could match through it. + always = true; + break; + } + keys.extend(selector_keys); + } + if !indexable { + // Every selector is impossible; the rule can never match. + continue; + } + if always || keys.is_empty() { + index.universal.push(rule_index); + } else { + for key in keys { + index.keys.entry(key).or_default().push(rule_index); + } + } + } + index + } +} + +#[derive(Clone)] +pub struct CascadeRule<'ir> { + /// Canonical selector text, for diagnostics and serialization. + pub selector: &'ir str, + pub parsed: crate::selectors::SelectorList<'ir>, + pub specificity: u32, + pub layer: Option<&'ir [String]>, + pub media: &'ir [bombshell_css_core::MediaCondition], + pub containers: &'ir [ContainerQuery], + pub supports: &'ir [bombshell_css_core::SupportsCondition], + pub declarations: &'ir [Declaration], + pub order: u32, +} + +impl<'ir> Stylesheet<'ir> { + /// Load a stylesheet from decoded IR. Selectors that fail to parse are + /// skipped (they are reported through [`Self::unmatched_selectors`]). + pub fn from_ir(ir: &'ir CompiledStylesheet, sheet_index: usize) -> Stylesheet<'ir> { + let mut rules = Vec::with_capacity(ir.rules.len()); + let mut parse_warnings = Vec::new(); + for rule in &ir.rules { + match parse_selector_list(rule.selector.as_str()) { + Ok(parsed) => rules.push(CascadeRule { + selector: rule.selector.as_str(), + parsed, + specificity: rule.specificity, + layer: rule.layer.as_deref(), + media: &rule.media, + containers: &rule.containers, + supports: &rule.supports, + declarations: &rule.declarations, + order: rule.order, + }), + Err(_) => parse_warnings.push(bombshell_css_core::Diagnostic::warning( + "selector-parse", + { + let mut message = + String::from("selector could not be matched at runtime: "); + message.push_str(&rule.selector); + message + }, + )), + } + } + let index = RuleIndex::build(&rules); + Stylesheet { + origin: ir.origin, + sheet_index, + rules, + layer_order: &ir.layer_order, + index, + parse_warnings, + } + } + + /// Rule indices that could possibly match `element`: the union of the + /// element's own index keys plus the always-checked bucket, deduped, + /// with impossible selectors filtered by an actual match attempt. + pub(crate) fn matched_rules( + &self, + element: &crate::element::MatchElement, + context: &mut crate::selectors::MatchContext, + ) -> Vec { + let node = element.node(); + let mut candidates: Vec = Vec::new(); + candidates.extend_from_slice(&self.index.universal); + let push_key = |key: &str, candidates: &mut Vec| { + if let Some(list) = self.index.keys.get(key) { + candidates.extend_from_slice(list); + } + }; + push_key(node.tag.as_str(), &mut candidates); + if let Some(id) = node.id.as_deref() { + push_key(id, &mut candidates); + } + for class in &node.classes { + push_key(class.as_str(), &mut candidates); + } + sort_by(&mut candidates, |a, b| a < b); + candidates.dedup(); + candidates.retain(|&i| context.matches(element, &self.rules[i as usize].parsed)); + candidates + } + + /// Selector text that failed to parse at load time. + pub fn unmatched_selectors(&self) -> impl Iterator { + self.parse_warnings + .iter() + .map(|d| &d.message["selector could not be matched at runtime: ".len()..]) + } + + fn layer_rank(&self, layer: Option<&[String]>) -> u32 { + match layer { + None => u32::MAX, + Some(path) => self + .layer_order + .iter() + .position(|p| p.as_slice() == path) + .map(|p| p as u32) + .unwrap_or(u32::MAX - 1), + } + } +} + + +/// Minimal stable sort. std's adaptive sort machinery is several KB of code; +/// cascade sorts are almost always a few dozen elements, where plain binary +/// insertion sort costs nothing and behaves identically. +fn sort_by(v: &mut [T], mut less: impl FnMut(&T, &T) -> bool) { + for i in 1..v.len() { + let mut j = i; + while j > 0 && less(&v[j], &v[j - 1]) { + j -= 1; + } + if j != i { + v[j..=i].rotate_right(1); + } + } +} +/// The computed style of one element: winning declarations with inheritance +/// applied and `var()` references resolved. Names always borrow the IR; +/// values borrow until substitution produces something new. +#[derive(Debug, Clone, Default, PartialEq)] +pub struct ComputedStyle<'ir> { + /// (property name, computed value), sorted by name. + pub declarations: Vec<(&'ir str, Cow<'ir, str>)>, +} + +impl<'ir> ComputedStyle<'ir> { + pub fn get(&self, name: &str) -> Option<&str> { + self.declarations + .iter() + .find(|(n, _)| *n == name) + .map(|(_, v)| v.as_ref()) + } + + pub(crate) fn from_map(map: StyleMap<'ir>) -> ComputedStyle<'ir> { + let mut declarations: Vec<(&'ir str, Cow<'ir, str>)> = map.into_iter().collect(); + sort_by(&mut declarations, |a, b| a < b); + ComputedStyle { declarations } + } +} + +/// Compute the style of `target` within `tree`. +/// +/// The full ancestor chain is cascaded so container lookups, inheritance, +/// and style queries see computed values. +pub fn compute<'ir>( + sheets: &[Stylesheet<'ir>], + tree: &TreeSnapshot, + target: usize, + env: &bombshell_css_core::Environment, +) -> Option> { + if target >= tree.nodes.len() { + return None; + } + compute_tree(sheets, tree, env, std::slice::from_ref(&target)) + .into_iter() + .nth(target) + .flatten() +} + +/// Compute styles for a whole tree in one pass: nodes are processed in +/// document order (parents before children), so each node's cascade runs +/// exactly once and every computed style is available to its descendants. +/// +/// Returns one entry per node, index-aligned with `tree.nodes`; `targets` +/// restricts which entries are resolved (an empty list resolves every +/// node). Unresolved entries are empty styles. +pub fn compute_tree<'ir>( + sheets: &[Stylesheet<'ir>], + tree: &TreeSnapshot, + env: &bombshell_css_core::Environment, + targets: &[usize], +) -> Vec>> { + let count = tree.nodes.len(); + // Everything when `targets` is empty; otherwise each target plus its + // ancestor chain (targets may not be leaves, so walk to the root). + let mut wanted = vec![targets.is_empty(); count]; + for target in targets { + let mut index = Some(*target); + while let Some(current) = index { + wanted[current] = true; + index = tree.parent_of(current); + } + } + + // Resolved maps per node, kept so descendants can inherit computed + // values; the sorted `ComputedStyle` form is built only for output. + let mut maps: Vec>> = Vec::with_capacity(count); + for index in 0..count { + if !wanted[index] { + maps.push(None); + continue; + } + let parent = tree.parent_of(index); + // Hosts list nodes in document order, so the parent is already + // processed; a malformed ordering degrades to no inheritance rather + // than panicking. + let inherited: StyleMap<'ir> = match parent.and_then(|p| maps.get(p).and_then(|m| m.as_ref())) { + Some(map) => map.clone(), + None => HashMap::default(), + }; + // Container ancestors: walk up through the already-computed maps. + // The node itself is never its own container. + let mut containers: Vec = Vec::new(); + let mut current = parent; + while let Some(ancestor) = current { + if let Some(map) = maps.get(ancestor).and_then(|m| m.as_ref()) { + if let Some(container_type) = computed_container_type(map) { + let node = &tree.nodes[ancestor]; + containers.push(ContainerAncestor { + name: map.get("container-name").map(|v| v.clone().into_owned()), + container_type, + width: node.width, + height: node.height, + }); + } + } + current = tree.parent_of(ancestor); + } + + let mut map: StyleMap<'ir> = cascade(sheets, tree, index, env, &containers); + apply_css_wide_keywords(&mut map, &inherited); + for (name, value) in &inherited { + let inherits = + name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == *name); + if inherits && !map.contains_key(*name) { + map.insert(*name, value.clone()); + } + } + // var() substitution is part of computing the value: descendants + // inherit computed values, not raw `var()` text. + resolve_vars_in_map(&mut map); + maps.push(Some(map)); + } + + maps.into_iter() + .map(|slot| slot.map(ComputedStyle::from_map)) + .collect() +} + +fn computed_container_type(map: &StyleMap<'_>) -> Option { + match map.get("container-type").map(|v| v.as_ref()) { + Some("size") => Some(ContainerType::Size), + Some("inline-size") => Some(ContainerType::InlineSize), + Some("normal") | Some("") | None => None, + Some(_) => None, + } +} + +/// Resolve var() references in every non-custom declaration (custom +/// properties keep their raw values until referenced). +fn resolve_vars_in_map(map: &mut StyleMap<'_>) { + let names: Vec<&str> = map.keys().copied().collect(); + for name in names { + if name.starts_with("--") { + continue; + } + if let Some(value) = map.get(name) { + if value.contains("var(") { + match resolve_vars(value, map) { + Some(resolved) => { + map.insert(name, Cow::Owned(resolved)); + } + None => { + // Invalid at computed-value time. + map.remove(name); + } + } + } + } + } +} + +/// Handle `inherit`, `initial`, `unset`, and `revert` keywords. +fn apply_css_wide_keywords<'ir>(map: &mut StyleMap<'ir>, inherited: &StyleMap<'ir>) { + let names: Vec<&str> = map.keys().copied().collect(); + for name in names { + let keyword = map.get(name).map(|v| v.trim()).unwrap_or_default(); + let inherits = name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == name); + match keyword { + "inherit" => match inherited.get(name) { + Some(value) => { + map.insert(name, value.clone()); + } + None => { + map.remove(name); + } + }, + "unset" => { + if inherits { + match inherited.get(name) { + Some(value) => { + map.insert(name, value.clone()); + } + None => { + map.remove(name); + } + } + } else { + map.remove(name); + } + } + // No initial-value table yet: `initial` and `revert` drop the + // declaration. + "initial" | "revert" => { + map.remove(name); + } + _ => {} + } + } +} + +/// Cascade one node: condition-filtered, selector-matched rules, sorted by +/// origin/importance, layer, specificity, then source order; last write per +/// property wins. +fn cascade<'ir>( + sheets: &[Stylesheet<'ir>], + tree: &TreeSnapshot, + node_index: usize, + env: &bombshell_css_core::Environment, + containers: &[ContainerAncestor], +) -> StyleMap<'ir> { + let Some(element) = crate::element::MatchElement::new(tree, node_index) else { + return HashMap::default(); + }; + + struct Candidate<'a, 'ir> { + rule: &'a CascadeRule<'ir>, + origin_rank: u32, + layer_rank: u32, + specificity: u32, + order: (usize, u32), + } + + // One match context per element: the nth-index cache is per-element + // state, not per-rule state. + let mut context = crate::selectors::MatchContext::new(); + let mut candidates: Vec = Vec::new(); + for sheet in sheets { + let mut matched = sheet.matched_rules(&element, &mut context); + + for rule_index in matched.drain(..) { + let rule = &sheet.rules[rule_index as usize]; + if !eval_supports_list(rule.supports) { + continue; + } + if !eval_media_list(rule.media, env) { + continue; + } + if !eval_container_list(rule.containers, containers) { + continue; + } + for declaration in rule.declarations { + candidates.push(Candidate { + rule, + origin_rank: origin_importance_rank(sheet.origin, declaration.important), + layer_rank: sheet.layer_rank(rule.layer), + specificity: rule.specificity, + order: (sheet.sheet_index, rule.order), + }); + } + } + } + + sort_by(&mut candidates, |a, b| { + (a.origin_rank, a.layer_rank, a.specificity, a.order) + < (b.origin_rank, b.layer_rank, b.specificity, b.order) + }); + + // Ascending order: later candidates overwrite earlier ones, so the map + // ends up holding the winning value per property. + let mut winners: StyleMap<'ir> = HashMap::default(); + for candidate in candidates { + for declaration in candidate.rule.declarations { + winners.insert( + declaration.name.as_str(), + Cow::Borrowed(declaration.value.as_str()), + ); + } + } + winners +} + +/// Cascade priority within (origin, importance), ascending. +fn origin_importance_rank(origin: Origin, important: bool) -> u32 { + match (origin, important) { + (Origin::UserAgent, false) => 0, + (Origin::User, false) => 1, + (Origin::Author, false) => 2, + (Origin::Author, true) => 3, + (Origin::User, true) => 4, + (Origin::UserAgent, true) => 5, + } +} diff --git a/experiments/css/crates/css-runtime/src/conditions.rs b/experiments/css/crates/css-runtime/src/conditions.rs new file mode 100644 index 0000000..c3fa2f4 --- /dev/null +++ b/experiments/css/crates/css-runtime/src/conditions.rs @@ -0,0 +1,415 @@ +//! Runtime evaluation of the typed IR condition trees against the +//! [`Environment`] and container snapshots. + +use bombshell_css_core::{ + ColorScheme, Compare, ContainerCondition, ContainerQuery, Environment, Feature, FeatureKind, + FeatureValue, MediaCondition, +}; + +/// A container discovered while walking the ancestor chain. Sizes come from +/// the node snapshot; container-ness comes from the ancestor's own computed +/// `container-type` / `container-name`. +#[derive(Debug, Clone)] +pub struct ContainerAncestor { + /// The container's `container-name`, if set. + pub name: Option, + /// Whether the container tracks inline size, block size, or both. + pub container_type: ContainerType, + pub width: Option, + pub height: Option, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ContainerType { + Normal, + Size, + InlineSize, +} + +/// Evaluate a whole `@media` condition list (ANDed). +pub fn eval_media_list(conditions: &[MediaCondition], env: &Environment) -> bool { + conditions.iter().all(|c| eval_media(c, env)) +} + +pub fn eval_media(condition: &MediaCondition, env: &Environment) -> bool { + match condition { + MediaCondition::And(conditions) => conditions.iter().all(|c| eval_media(c, env)), + MediaCondition::Or(conditions) => conditions.iter().any(|c| eval_media(c, env)), + MediaCondition::Not(inner) => !eval_media(inner, env), + MediaCondition::Feature(feature) => eval_feature(feature, &FeatureSource::Viewport(env)), + MediaCondition::Unknown => false, + } +} + +/// Evaluate a whole `@container` condition list (ANDed). +pub fn eval_container_list(queries: &[ContainerQuery], containers: &[ContainerAncestor]) -> bool { + queries + .iter() + .all(|query| eval_container(query, containers)) +} + +pub fn eval_container(query: &ContainerQuery, containers: &[ContainerAncestor]) -> bool { + let container = match &query.name { + Some(name) => containers.iter().rev().find(|c| { + c.container_type != ContainerType::Normal + && c.name.as_deref().is_some_and(|n| n == name) + }), + None => containers + .iter() + .rev() + .find(|c| c.container_type != ContainerType::Normal), + }; + let Some(container) = container else { + return false; + }; + eval_container_condition(&query.condition, container) +} + +fn eval_container_condition(condition: &ContainerCondition, container: &ContainerAncestor) -> bool { + match condition { + ContainerCondition::And(conditions) => conditions + .iter() + .all(|c| eval_container_condition(c, container)), + ContainerCondition::Or(conditions) => conditions + .iter() + .any(|c| eval_container_condition(c, container)), + ContainerCondition::Not(inner) => !eval_container_condition(inner, container), + ContainerCondition::Size(feature) => { + eval_feature(feature, &FeatureSource::Container(container)) + } + ContainerCondition::Style { text } => { + // Style queries are resolved against the container's computed + // declarations by the cascade layer; without that context they + // do not match. + let _ = text; + false + } + ContainerCondition::Unknown => false, + } +} + +pub enum FeatureSource<'a> { + Viewport(&'a Environment), + Container(&'a ContainerAncestor), +} + +impl FeatureSource<'_> { + fn dimension(&self, name: &str) -> Option { + match self { + FeatureSource::Viewport(env) => match name { + "width" | "inline-size" => env.viewport_width, + "height" | "block-size" => env.viewport_height, + _ => None, + }, + FeatureSource::Container(container) => { + let (width, height) = (container.width, container.height); + match name { + "width" => width, + "height" => height, + "inline-size" => match container.container_type { + // A `size` container has both axes; an `inline-size` + // container only guarantees the inline axis. + ContainerType::Normal => None, + ContainerType::InlineSize | ContainerType::Size => width, + }, + "block-size" => match container.container_type { + ContainerType::Size => height, + _ => None, + }, + _ => None, + } + } + } + } +} + +/// Evaluate one media/container size feature. +pub fn eval_feature(feature: &Feature, source: &FeatureSource) -> bool { + let name = feature.name.as_str(); + + // Orientation and aspect-ratio need special handling. + if let Some(result) = eval_special_feature(name, feature, source) { + return result; + } + + // Environment-driven features (prefers-*, color, hover, ...). + if let FeatureSource::Viewport(env) = source { + if let Some(result) = eval_env_feature(name, feature, env) { + return result; + } + } + + // Dimensional features. + match &feature.kind { + FeatureKind::Boolean => { + // `(width)` is true when the dimension is known and non-zero. + source.dimension(name).is_some_and(|v| v != 0.0) + } + FeatureKind::Plain { value } => { + let Some(dimension) = source.dimension(name) else { + return false; + }; + let Some(actual) = feature_value(value, source) else { + return false; + }; + compare(Compare::Equal, dimension, actual) + } + FeatureKind::Range { cmp, value } => { + let Some(dimension) = source.dimension(name) else { + return false; + }; + let Some(actual) = feature_value(value, source) else { + return false; + }; + compare(*cmp, dimension, actual) + } + FeatureKind::Interval { + start, + start_cmp, + end, + end_cmp, + } => { + let Some(dimension) = source.dimension(name) else { + return false; + }; + let (Some(start), Some(end)) = + (feature_value(start, source), feature_value(end, source)) + else { + return false; + }; + // `(a < feature < b)`: the start operator is written between the + // start value and the feature (feature on the right), the end + // operator between the feature and the end value. + compare(*start_cmp, start, dimension) && compare(*end_cmp, dimension, end) + } + } +} + +/// Orientation and aspect-ratio need special handling. +fn eval_special_feature(name: &str, feature: &Feature, source: &FeatureSource) -> Option { + if name == "orientation" { + let (width, height) = match source { + FeatureSource::Viewport(env) => (env.viewport_width, env.viewport_height), + FeatureSource::Container(c) => (c.width, c.height), + }; + let (Some(width), Some(height)) = (width, height) else { + return Some(false); + }; + let landscape = width > height; + return Some(match &feature.kind { + FeatureKind::Boolean => true, + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => match ident.as_str() { + "landscape" => landscape, + "portrait" => !landscape, + _ => false, + }, + _ => false, + }); + } + if name == "aspect-ratio" { + let (width, height) = match source { + FeatureSource::Viewport(env) => (env.viewport_width, env.viewport_height), + FeatureSource::Container(c) => (c.width, c.height), + }; + let (Some(width), Some(height)) = (width, height) else { + return Some(false); + }; + let actual = width / height.max(f32::EPSILON); + return Some(match &feature.kind { + FeatureKind::Boolean => true, + FeatureKind::Plain { value } => match feature_value(value, source) { + Some(ratio) => (actual - ratio).abs() < f32::EPSILON, + None => false, + }, + FeatureKind::Range { cmp, value } => match feature_value(value, source) { + Some(ratio) => compare(*cmp, actual, ratio), + None => false, + }, + FeatureKind::Interval { + start, + start_cmp, + end, + end_cmp, + } => { + let (Some(start), Some(end)) = + (feature_value(start, source), feature_value(end, source)) + else { + return Some(false); + }; + compare(*start_cmp, actual, start) && compare(*end_cmp, actual, end) + } + }); + } + None +} + +/// Environment-driven features that are not plain dimension comparisons. +fn eval_env_feature(name: &str, feature: &Feature, env: &Environment) -> Option { + match name { + "prefers-color-scheme" => Some(match &feature.kind { + FeatureKind::Boolean => true, + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => match ident.as_str() { + "light" => env.color_scheme == ColorScheme::Light, + "dark" => env.color_scheme == ColorScheme::Dark, + _ => false, + }, + _ => false, + }), + "prefers-reduced-motion" => Some(match &feature.kind { + FeatureKind::Boolean => true, + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => match ident.as_str() { + "reduce" => env.reduced_motion, + "no-preference" => !env.reduced_motion, + _ => false, + }, + _ => false, + }), + "resolution" + | "device-pixel-ratio" + | "min-device-pixel-ratio" + | "max-device-pixel-ratio" => { + let dppx = match &feature.kind { + FeatureKind::Plain { value } => feature_value(value, &FeatureSource::Viewport(env)), + FeatureKind::Range { cmp, value } => { + return Some(match feature_value(value, &FeatureSource::Viewport(env)) { + Some(actual) => compare(*cmp, env.resolution, actual), + None => false, + }); + } + FeatureKind::Boolean => return Some(true), + FeatureKind::Interval { .. } => return Some(false), + }; + Some(match dppx { + Some(actual) => (env.resolution - actual).abs() < f32::EPSILON, + None => false, + }) + } + "color" => Some(true), + "color-index" | "monochrome" | "grid" => Some(match &feature.kind { + FeatureKind::Boolean => false, + FeatureKind::Plain { value } => { + match feature_value(value, &FeatureSource::Viewport(env)) { + Some(actual) => actual == 0.0, + None => false, + } + } + _ => false, + }), + "hover" | "any-hover" | "pointer" | "any-pointer" => Some(match &feature.kind { + FeatureKind::Boolean => false, + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => matches!(ident.as_str(), "none"), + _ => false, + }), + "color-gamut" => Some(match &feature.kind { + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => matches!(ident.as_str(), "srgb"), + _ => false, + }), + "scripting" => Some(match &feature.kind { + FeatureKind::Boolean => true, + FeatureKind::Plain { + value: FeatureValue::Ident(ident), + } => matches!(ident.as_str(), "enabled" | "initial-only"), + _ => false, + }), + "update" + | "overflow-block" + | "overflow-inline" + | "dynamic-range" + | "video-dynamic-range" + | "prefers-contrast" + | "forced-colors" + | "prefers-transparency" + | "inverted-colors" => Some(match &feature.kind { + FeatureKind::Boolean => false, + _ => false, + }), + _ => None, + } +} + +fn feature_value(value: &FeatureValue, source: &FeatureSource) -> Option { + match value { + FeatureValue::Px(px) => Some(*px), + FeatureValue::RelativeLength { value, unit } => { + let base = match source { + // Media queries resolve em/rem against the root font size. + FeatureSource::Viewport(_) => 16.0, + // Container queries resolve against the container's font + // size, which the terminal model does not carry yet. + FeatureSource::Container(_) => 16.0, + }; + match unit.as_str() { + "em" | "rem" => Some(value * base), + "ch" | "ex" => Some(value * 8.0), + "vw" => match source { + FeatureSource::Viewport(env) => env.viewport_width.map(|w| value * w / 100.0), + FeatureSource::Container(_) => None, + }, + "vh" => match source { + FeatureSource::Viewport(env) => env.viewport_height.map(|h| value * h / 100.0), + FeatureSource::Container(_) => None, + }, + "cqw" | "cqi" => match source { + FeatureSource::Container(c) => c.width.map(|w| value * w / 100.0), + FeatureSource::Viewport(_) => None, + }, + "cqh" | "cqb" => match source { + FeatureSource::Container(c) => c.height.map(|h| value * h / 100.0), + FeatureSource::Viewport(_) => None, + }, + _ => None, + } + } + FeatureValue::Number(n) => Some(*n), + FeatureValue::Integer(i) => Some(*i as f32), + FeatureValue::Boolean(b) => Some((*b as i32) as f32), + FeatureValue::Dppx(d) => Some(*d), + FeatureValue::Ratio(numerator, denominator) => { + if *denominator == 0.0 { + None + } else { + Some(numerator / denominator) + } + } + FeatureValue::Ident(_) => None, + FeatureValue::Unknown => None, + } +} + +fn compare(cmp: Compare, actual: f32, expected: f32) -> bool { + match cmp { + Compare::Equal => (actual - expected).abs() < f32::EPSILON, + Compare::GreaterThan => actual > expected, + Compare::GreaterThanEqual => actual >= expected, + Compare::LessThan => actual < expected, + Compare::LessThanEqual => actual <= expected, + } +} + +/// Evaluate a `@supports` condition (leaves were pre-evaluated by the +/// compiler). +pub fn eval_supports_list(conditions: &[bombshell_css_core::SupportsCondition]) -> bool { + conditions.iter().all(eval_supports) +} + +pub fn eval_supports(condition: &bombshell_css_core::SupportsCondition) -> bool { + use bombshell_css_core::SupportsCondition as C; + match condition { + C::And(conditions) => conditions.iter().all(eval_supports), + C::Or(conditions) => conditions.iter().any(eval_supports), + C::Not(inner) => !eval_supports(inner), + C::Declaration { supported, .. } => *supported, + C::Selector { supported, .. } => *supported, + C::Unknown => false, + } +} diff --git a/experiments/css/crates/css-runtime/src/element.rs b/experiments/css/crates/css-runtime/src/element.rs new file mode 100644 index 0000000..5255db7 --- /dev/null +++ b/experiments/css/crates/css-runtime/src/element.rs @@ -0,0 +1,414 @@ +//! The `parcel_selectors::Element` implementation over the flat +//! [`TreeSnapshot`], plus the runtime semantics for pseudo-classes, +//! `:has()` traversal, and `:nth-child(.. of S)` counting. + +use crate::selectors::{PseudoClass, Selectors, matches_relative, nth_matches}; +use bombshell_css_core::{NO_PARENT, NodeSnapshot, TreeSnapshot}; +use parcel_selectors::attr::CaseSensitivity; +use parcel_selectors::attr::{AttrSelectorOperation, NamespaceConstraint}; +use parcel_selectors::context::MatchingContext; +use parcel_selectors::matching::ElementSelectorFlags; +use parcel_selectors::parser::{NthOfSelectorData, NthType}; +use parcel_selectors::{Element as ParcelElement, OpaqueElement}; + +/// A view of one node in a [`TreeSnapshot`], cheap to clone. +#[derive(Clone, Copy, Debug)] +pub struct MatchElement<'a> { + pub tree: &'a TreeSnapshot, + pub index: usize, +} + +impl<'a> MatchElement<'a> { + pub fn new(tree: &'a TreeSnapshot, index: usize) -> Option { + (index < tree.nodes.len()).then_some(MatchElement { tree, index }) + } + + pub fn node(&self) -> &'a NodeSnapshot { + &self.tree.nodes[self.index] + } + + pub(crate) fn attr(&self, name: &str) -> Option<&'a str> { + self.node() + .attributes + .iter() + .find(|(n, _)| n == name) + .map(|(_, v)| v.as_str()) + } + + /// Resolve an inheritable-ish attribute by walking up the chain + /// (used for `lang` and `dir`). + fn inherited_attr(&self, name: &str) -> Option<&'a str> { + let mut current = *self; + loop { + if let Some(value) = current.attr(name) { + return Some(value); + } + match current.parent_element() { + Some(parent) => current = parent, + None => return None, + } + } + } + + /// Sibling indices sharing this node's parent (contiguous run). + fn siblings(&self) -> impl Iterator + '_ { + let parent = self.tree.parents.get(self.index).copied(); + let tree = self.tree; + let mut start = self.index; + while start > 0 && tree.parents.get(start - 1).copied() == parent { + start -= 1; + } + let mut end = self.index; + while end + 1 < tree.nodes.len() && tree.parents.get(end + 1).copied() == parent { + end += 1; + } + start..=end + } + + /// All descendants (depth first, document order). + fn descendants(&self) -> impl Iterator + '_ { + let tree = self.tree; + let mut stack: Vec = Vec::new(); + // Collect children eagerly (reverse for DFS order). + let children: Vec = tree + .parents + .iter() + .enumerate() + .filter(|&(_, p)| *p == self.index as u32) + .map(|(i, _)| i) + .collect(); + for child in children.iter().rev() { + stack.push(*child); + } + std::iter::from_fn(move || { + let index = stack.pop()?; + for child in tree + .parents + .iter() + .enumerate() + .filter(|&(_, p)| *p == index as u32) + .map(|(i, _)| i) + .collect::>() + .into_iter() + .rev() + { + stack.push(child); + } + Some(index) + }) + } + + /// Following siblings of this element and all of their descendants. + fn following_siblings_and_descendants(&self) -> impl Iterator + '_ { + let tree = self.tree; + let parent = self.tree.parents.get(self.index).copied(); + let following: Vec = (self.index + 1..tree.nodes.len()) + .filter(|&i| tree.parents.get(i).copied() == parent) + .collect(); + following.into_iter().flat_map(move |sibling| { + std::iter::once(sibling).chain( + MatchElement::new(tree, sibling) + .map(|el| el.descendants().collect::>()) + .unwrap_or_default(), + ) + }) + } + + fn has_focus_within(&self) -> bool { + if self.node().focus { + return true; + } + self.descendants().any(|i| self.tree.nodes[i].focus) + } + + /// Candidates addressable by `:has()` relative selectors: everything + /// anchored at this element — its descendants, its following siblings, + /// and the descendants of those siblings. + fn has_candidates(&self) -> impl Iterator> + '_ { + self.descendants() + .chain(self.following_siblings_and_descendants()) + .filter_map(move |index| MatchElement::new(self.tree, index)) + } + + fn matches_any_of( + &self, + selectors: &[crate::selectors::Selector], + scope: &MatchElement, + ) -> bool { + selectors + .iter() + .any(|selector| matches_relative(selector, self, scope)) + } + + fn nth_of_matches(&self, data: &NthOfSelectorData<'_, Selectors>) -> bool { + let nth = data.nth_data(); + if matches!(nth.ty, NthType::Col | NthType::LastCol) { + return false; + } + let mut nth_cache = parcel_selectors::NthIndexCache::default(); + let mut context = MatchingContext::new( + parcel_selectors::context::MatchingMode::Normal, + None, + Some(&mut nth_cache), + parcel_selectors::context::QuirksMode::NoQuirks, + ); + let mut flags = |_: &MatchElement, _: ElementSelectorFlags| {}; + + let of_selectors = data.selectors(); + let mut matches_of = |index: usize| -> bool { + MatchElement::new(self.tree, index).is_some_and(|el| { + of_selectors.iter().any(|selector| { + parcel_selectors::matching::matches_selector( + selector, + 0, + None, + &el, + &mut context, + &mut flags, + ) + }) + }) + }; + + let from_end = matches!(nth.ty, NthType::LastChild | NthType::LastOfType); + let of_type = matches!(nth.ty, NthType::OfType | NthType::LastOfType); + + let mut matching: Vec = Vec::new(); + for sibling in self.siblings() { + if of_type && self.tree.nodes[sibling].tag != self.node().tag { + continue; + } + if !of_selectors.is_empty() && !matches_of(sibling) { + continue; + } + matching.push(sibling); + } + + let count = if from_end { + matching + .iter() + .rposition(|&i| i == self.index) + .map(|p| p + 1) + } else { + matching + .iter() + .position(|&i| i == self.index) + .map(|p| p + 1) + }; + let Some(count) = count else { return false }; + nth_matches(data.nth_data(), count as i32) + } +} + +impl<'i, 'a> ParcelElement<'i> for MatchElement<'a> { + type Impl = Selectors; + + fn opaque(&self) -> OpaqueElement { + OpaqueElement::new(&self.tree.nodes[self.index]) + } + + fn parent_element(&self) -> Option { + self.tree + .parent_of(self.index) + .and_then(|index| MatchElement::new(self.tree, index)) + } + + fn parent_node_is_shadow_root(&self) -> bool { + false + } + + fn containing_shadow_host(&self) -> Option { + None + } + + fn is_pseudo_element(&self) -> bool { + false + } + + fn prev_sibling_element(&self) -> Option { + let parent = self.tree.parents.get(self.index).copied(); + (self.index > 0 && self.tree.parents.get(self.index - 1).copied() == parent).then(|| { + MatchElement { + tree: self.tree, + index: self.index - 1, + } + }) + } + + fn next_sibling_element(&self) -> Option { + let parent = self.tree.parents.get(self.index).copied(); + (self.index + 1 < self.tree.nodes.len() + && self.tree.parents.get(self.index + 1).copied() == parent) + .then(|| MatchElement { + tree: self.tree, + index: self.index + 1, + }) + } + + fn is_html_element_in_html_document(&self) -> bool { + false + } + + fn has_local_name(&self, local_name: &str) -> bool { + self.node().tag == local_name + } + + fn has_namespace(&self, ns: &str) -> bool { + ns.is_empty() + } + + fn is_same_type(&self, other: &Self) -> bool { + self.node().tag == other.node().tag + } + + fn attr_matches( + &self, + ns: &NamespaceConstraint< + &>::NamespaceUrl, + >, + local_name: &>::LocalName, + operation: &AttrSelectorOperation< + &>::AttrValue, + >, + ) -> bool { + let namespace_ok = match ns { + NamespaceConstraint::Any => true, + // Elements live in no namespace; only the empty URL can match. + NamespaceConstraint::Specific(url) => url.as_ref().is_empty(), + }; + if !namespace_ok { + return false; + } + let name = local_name.as_ref(); + let Some(value) = self.attr(name) else { + return false; + }; + // `eval_str` implements the full operator semantics (including the + // `i`/`s` flags resolved by the parser). + operation.eval_str(value) + } + + fn match_non_ts_pseudo_class( + &self, + pc: &PseudoClass<'i>, + _context: &mut MatchingContext<'_, 'i, Selectors>, + _flags_setter: &mut F, + ) -> bool + where + F: FnMut(&Self, ElementSelectorFlags), + { + let node = self.node(); + match pc { + PseudoClass::Hover => node.hover, + PseudoClass::Active => node.active, + PseudoClass::Focus => node.focus, + PseudoClass::FocusVisible => node.focus, + PseudoClass::FocusWithin => self.has_focus_within(), + PseudoClass::Disabled => self.attr("disabled").is_some(), + PseudoClass::Enabled => self.attr("disabled").is_none(), + PseudoClass::Checked => self.attr("checked").is_some(), + PseudoClass::Required => self.attr("required").is_some(), + PseudoClass::Optional => self.attr("required").is_none(), + PseudoClass::ReadOnly => { + self.attr("readonly").is_some() || self.attr("disabled").is_some() + } + PseudoClass::ReadWrite => { + self.attr("readonly").is_none() && self.attr("disabled").is_none() + } + PseudoClass::PlaceholderShown => self.attr("placeholder").is_some(), + PseudoClass::Lang(languages) => self + .inherited_attr("lang") + .is_some_and(|lang| languages.iter().any(|want| lang_matches(lang, want))), + PseudoClass::Dir(dir) => self + .inherited_attr("dir") + .is_some_and(|value| value.eq_ignore_ascii_case(&*dir)), + PseudoClass::NthOf(data) => self.nth_of_matches(data), + PseudoClass::Has(selectors) => self + .has_candidates() + .any(|candidate| candidate.matches_any_of(selectors, self)), + PseudoClass::NeverMatches => false, + } + } + + fn match_pseudo_element( + &self, + _pe: &crate::selectors::PseudoElement, + _context: &mut MatchingContext<'_, 'i, Selectors>, + ) -> bool { + false + } + + fn is_link(&self) -> bool { + false + } + + fn is_html_slot_element(&self) -> bool { + false + } + + fn has_id( + &self, + id: &>::Identifier, + case_sensitivity: CaseSensitivity, + ) -> bool { + self.node() + .id + .as_deref() + .is_some_and(|value| case_eq(case_sensitivity, value, id.as_ref())) + } + + fn has_class( + &self, + name: &>::Identifier, + case_sensitivity: CaseSensitivity, + ) -> bool { + self.node() + .classes + .iter() + .any(|class| case_eq(case_sensitivity, class, name.as_ref())) + } + + fn imported_part( + &self, + _name: &>::Identifier, + ) -> Option<>::Identifier> { + None + } + + fn is_part( + &self, + _name: &>::Identifier, + ) -> bool { + false + } + + fn is_empty(&self) -> bool { + !self.tree.parents.contains(&(self.index as u32)) + } + + fn is_root(&self) -> bool { + matches!(self.tree.parents.get(self.index), None | Some(&NO_PARENT)) + } + + fn ignores_nth_child_selectors(&self) -> bool { + false + } +} + +fn case_eq(case_sensitivity: CaseSensitivity, a: &str, b: &str) -> bool { + match case_sensitivity { + CaseSensitivity::CaseSensitive => a == b, + CaseSensitivity::AsciiCaseInsensitive => a.eq_ignore_ascii_case(b), + } +} + +/// CSS `lang()` matching: exact (case-insensitive) or wildcard prefix on `-`. +fn lang_matches(actual: &str, wanted: &str) -> bool { + let actual = actual.trim(); + let wanted = wanted.trim(); + actual.eq_ignore_ascii_case(wanted) + || (actual + .get(..wanted.len()) + .is_some_and(|prefix| prefix.eq_ignore_ascii_case(wanted)) + && actual.as_bytes().get(wanted.len()) == Some(&b'-')) +} diff --git a/experiments/css/crates/css-runtime/src/lib.rs b/experiments/css/crates/css-runtime/src/lib.rs new file mode 100644 index 0000000..9eb07de --- /dev/null +++ b/experiments/css/crates/css-runtime/src/lib.rs @@ -0,0 +1,38 @@ +//! Selector matching and cascade for the Bombshell CSS pipeline. +//! +//! The runtime consumes the versioned stylesheet IR emitted by +//! `bombshell-css-compiler` (or any host that produces the same contract) +//! and resolves computed styles against a flat DOM [`TreeSnapshot`]: +//! +//! - selector matching via `parcel_selectors`, the engine lightningcss +//! itself uses, adapted to the snapshot model (see [`selectors`] and +//! [`element`]); +//! - cascade by origin/importance, `@layer`, specificity, and source order +//! (see [`cascade`]); +//! - inheritance, custom properties, `var()` substitution, and CSS-wide +//! keywords; +//! - `@media`, `@container`, and `@supports` condition evaluation against +//! the [`Environment`] (see [`conditions`]). + +pub mod cascade; +pub mod conditions; +pub mod element; +pub mod selectors; +pub mod values; + +pub use bombshell_css_core::{Environment, NodeSnapshot, TreeSnapshot}; + +pub use cascade::{ComputedStyle, Stylesheet}; +pub use conditions::{ContainerAncestor, ContainerType}; +pub use element::MatchElement; +pub use selectors::{SelectorList, parse_selector_list}; + +/// Resolve the computed style for `target`. +pub fn compute<'ir>( + sheets: &[Stylesheet<'ir>], + tree: &TreeSnapshot, + target: usize, + env: &Environment, +) -> Option> { + cascade::compute(sheets, tree, target, env) +} diff --git a/experiments/css/crates/css-runtime/src/selectors.rs b/experiments/css/crates/css-runtime/src/selectors.rs new file mode 100644 index 0000000..3908e71 --- /dev/null +++ b/experiments/css/crates/css-runtime/src/selectors.rs @@ -0,0 +1,40 @@ +//! Matching glue over the selector engine (which lives in `bombshell-css-core` +//! so the compiler and runtime share one parser). + +pub use bombshell_css_core::selectors::*; + +use crate::element::MatchElement; +use parcel_selectors::matching::matches_selector; +use parcel_selectors::parser::{NthSelectorData, NthType}; +use parcel_selectors::{Element as ParcelElement, NthIndexCache}; + +/// Match one relative selector (from `:has()`) with `scope` pinned to the +/// anchoring element. +pub(crate) fn matches_relative( + selector: &Selector, + candidate: &MatchElement, + scope: &MatchElement, +) -> bool { + let mut nth_cache = NthIndexCache::default(); + let mut context = parcel_selectors::context::MatchingContext::new( + parcel_selectors::context::MatchingMode::Normal, + None, + Some(&mut nth_cache), + parcel_selectors::context::QuirksMode::NoQuirks, + ); + context.scope_element = Some(scope.opaque()); + let mut flags = |_: &MatchElement, _: parcel_selectors::matching::ElementSelectorFlags| {}; + matches_selector(selector, 0, None, candidate, &mut context, &mut flags) +} + +/// Nth arithmetic shared by the `NthOf` evaluator. +pub(crate) fn nth_matches(nth: &NthSelectorData, count: i32) -> bool { + if matches!(nth.ty, NthType::Col | NthType::LastCol) { + return false; + } + if nth.a == 0 { + return count == nth.b; + } + let delta = count - nth.b; + delta % nth.a == 0 && delta / nth.a >= 0 +} diff --git a/experiments/css/crates/css-runtime/src/values.rs b/experiments/css/crates/css-runtime/src/values.rs new file mode 100644 index 0000000..3e1a789 --- /dev/null +++ b/experiments/css/crates/css-runtime/src/values.rs @@ -0,0 +1,233 @@ +//! `var()` substitution against resolved custom properties. +//! +//! Values are canonical CSS text; substitution walks the token stream so +//! strings and nested functions are handled correctly. Cycles resolve to +//! `None` (the declaration becomes invalid at computed-value time and the +//! caller falls back to inheritance). + +use bombshell_css_core::tokens::write_token; +use cssparser::{Parser as CssParser, ParserInput, Token}; + +use crate::cascade::StyleMap; + +/// Resolve every `var(--x[, fallback])` in `value` against `custom`. +/// +/// Returns `None` when a referenced variable is missing (no fallback) or a +/// cycle is detected, mirroring "invalid at computed-value time". +pub fn resolve_vars(value: &str, custom: &StyleMap<'_>) -> Option { + if !value.contains("var(") { + return Some(value.to_string()); + } + let mut resolving = Vec::new(); + resolve_value(value, custom, &mut resolving, 0) +} + +fn resolve_value( + value: &str, + custom: &StyleMap<'_>, + resolving: &mut Vec, + depth: u32, +) -> Option { + if depth > 32 { + return None; + } + if !value.contains("var(") { + return Some(value.to_string()); + } + + let mut input = ParserInput::new(value); + let mut parser = CssParser::new(&mut input); + let mut out = String::new(); + + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Function(name) if name.eq_ignore_ascii_case("var") => { + let substitution = parse_var_arguments(&mut parser, custom, resolving, depth)?; + out.push_str(&substitution); + } + Token::Function(name) => { + out.push_str(name); + out.push('('); + let inner = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens(parser, &mut out, custom, resolving, depth); + Ok(()) + }, + ); + let _ = inner; + out.push(')'); + } + Token::ParenthesisBlock => { + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens(parser, &mut out, custom, resolving, depth); + Ok(()) + }, + ); + out.push(')'); + } + other => write_token(&mut out, &other), + }, + Err(_) => break, + } + } + Some(out) +} + +/// Read the arguments of `var(name, fallback?)` and produce the substituted +/// text. +fn parse_var_arguments( + parser: &mut CssParser, + custom: &StyleMap<'_>, + resolving: &mut Vec, + depth: u32, +) -> Option { + let mut name: Option = None; + let mut fallback = String::new(); + let mut in_fallback = false; + + let _ = parser.parse_nested_block(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Ident(ident) if name.is_none() && !in_fallback => { + name = Some(ident.to_string()); + } + Token::Comma if !in_fallback => { + in_fallback = true; + } + other => { + if !in_fallback && name.is_some() { + // Junk between the name and the comma makes + // the var() invalid. + return Err(cssparser::ParseError { + kind: cssparser::ParseErrorKind::Basic( + cssparser::BasicParseErrorKind::UnexpectedToken(other.clone()), + ), + location: parser.current_source_location(), + }); + } + // The fallback may itself contain var() refs and + // nested functions; descend like the main path. + match other { + Token::Function(fn_name) if fn_name.eq_ignore_ascii_case("var") => { + let substitution = + match parse_var_arguments(parser, custom, resolving, depth) { + Some(text) => text, + None => return Ok(()), + }; + fallback.push_str(&substitution); + } + Token::Function(fn_name) => { + fallback.push_str(fn_name); + fallback.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens( + parser, + &mut fallback, + custom, + resolving, + depth, + ); + Ok(()) + }, + ); + fallback.push(')'); + } + Token::ParenthesisBlock => { + fallback.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens( + parser, + &mut fallback, + custom, + resolving, + depth, + ); + Ok(()) + }, + ); + fallback.push(')'); + } + inner => write_token(&mut fallback, &inner), + } + } + }, + Err(_) => break, + } + } + Ok(()) + }); + + let name = name?; + if !name.starts_with("--") { + return None; + } + if resolving.contains(&name) { + return None; + } + + if let Some(value) = custom.get(name.as_str()) { + resolving.push(name); + let resolved = resolve_value(value, custom, resolving, depth + 1); + resolving.pop(); + return resolved; + } + + if in_fallback { + resolve_value(fallback.trim(), custom, resolving, depth + 1) + } else { + None + } +} + +/// Serialize the remaining tokens of a nested block into `out`, resolving +/// any `var()` references along the way. +fn write_tokens( + parser: &mut CssParser, + out: &mut String, + custom: &StyleMap<'_>, + resolving: &mut Vec, + depth: u32, +) { + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Function(name) if name.eq_ignore_ascii_case("var") => { + let substitution = match parse_var_arguments(parser, custom, resolving, depth) { + Some(text) => text, + None => return, + }; + out.push_str(&substitution); + } + Token::Function(name) => { + out.push_str(name); + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens(parser, out, custom, resolving, depth); + Ok(()) + }, + ); + out.push(')'); + } + Token::ParenthesisBlock => { + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_tokens(parser, out, custom, resolving, depth); + Ok(()) + }, + ); + out.push(')'); + } + other => write_token(out, &other), + }, + Err(_) => break, + } + } +} diff --git a/experiments/css/crates/css-runtime/tests/runtime.rs b/experiments/css/crates/css-runtime/tests/runtime.rs new file mode 100644 index 0000000..50f5b0e --- /dev/null +++ b/experiments/css/crates/css-runtime/tests/runtime.rs @@ -0,0 +1,567 @@ +//! End-to-end runtime tests: compile CSS, load the IR, and resolve computed +//! styles against tree snapshots. + +use bombshell_css_compiler::compile; +use bombshell_css_core::{Environment, NO_PARENT, NodeSnapshot, Origin, TreeSnapshot}; +use bombshell_css_runtime::{Stylesheet, compute}; + +fn node(tag: &str) -> NodeSnapshot { + NodeSnapshot { + tag: tag.to_string(), + ..Default::default() + } +} + +fn tree(nodes: Vec, parents: Vec) -> TreeSnapshot { + TreeSnapshot { nodes, parents } +} + +/// An owned compiled case; [`Case::sheet`] borrows the parsed stylesheet +/// from the IR it keeps alive. +struct Case { + ir: bombshell_css_core::CompiledStylesheet, + index: usize, +} + +impl Case { + fn sheet(&self) -> Stylesheet<'_> { + Stylesheet::from_ir(&self.ir, self.index) + } +} + +fn stylesheet(css: &str) -> Case { + let out = compile(css, Origin::Author, Default::default()); + assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics); + Case { + ir: out.sheet.expect("sheet"), + index: 0, + } +} + +fn get<'a>(style: &'a bombshell_css_runtime::ComputedStyle, name: &str) -> &'a str { + style + .get(name) + .unwrap_or_else(|| panic!("missing {name}: {:?}", style.declarations)) +} + +#[test] +fn matches_selectors() { + let css = r" + box { color: red } + .panel { color: green } + #main { color: blue } + .a.b { color: yellow } + [disabled] { opacity: 0.5 } + [role=tab] { text-transform: uppercase } + [data-x^='pre'] { text-decoration: underline } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + node("box"), + { + let mut n = node("div"); + n.classes = vec!["panel".into(), "a".into(), "b".into()]; + n.attributes = vec![ + ("disabled".into(), "".into()), + ("role".into(), "tab".into()), + ("data-x".into(), "prefix-1".into()), + ]; + n + }, + { + let mut n = node("span"); + n.id = Some("main".into()); + n + }, + ], + vec![NO_PARENT, NO_PARENT, NO_PARENT], + ); + let env = Environment::default(); + + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "red" + ); + // .a.b wins over .panel (higher specificity). + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + "color" + ), + "#ff0" + ); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + "opacity" + ), + ".5" + ); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + "text-transform" + ), + "uppercase" + ); + // [data-x^='pre'] also matched. + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + "text-decoration" + ), + "underline" + ); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(), + "color" + ), + "#00f" + ); +} + +#[test] +fn matches_combinators_and_structural_pseudos() { + let css = r" + .list > .item { color: red } + .root .item { color: green } + .item:first-child { color: blue } + .item:last-child { color: yellow } + .item + .item { font-weight: bold } + .item:nth-child(2n) { opacity: 0.5 } + .item:only-child { visibility: hidden } + .root:has(.special) .item { text-decoration: line-through } + .item:not(.skip) { cursor: pointer } + .wrap:is(.a, .b) .item { text-align: center } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + { + let mut n = node("root"); + n.classes = vec!["root".into()]; + n + }, // 0 + { + let mut n = node("wrap"); + n.classes = vec!["wrap".into(), "a".into()]; + n + }, // 1 + { + let mut n = node("list"); + n.classes = vec!["list".into()]; + n + }, // 2 + { + let mut n = node("item"); + n.classes = vec!["item".into()]; + n + }, // 3 + { + let mut n = node("item"); + n.classes = vec!["item".into()]; + n + }, // 4 + { + let mut n = node("item"); + n.classes = vec!["item".into(), "special".into()]; + n + }, // 5 + ], + vec![NO_PARENT, 0, 1, 2, 2, 2], + ); + let env = Environment::default(); + + // item 3: first-child; ancestor `.root:has(.special)` styles all items + let s3 = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + assert_eq!(get(&s3, "color"), "#00f"); + assert_eq!(get(&s3, "cursor"), "pointer"); + assert_eq!(get(&s3, "text-align"), "center"); + assert_eq!(get(&s3, "text-decoration"), "line-through"); + // no `+` previous sibling → no bold + assert!(s3.get("font-weight").is_none()); + + // item 4: nth-child(2), has previous sibling → bold + let s4 = compute(std::slice::from_ref(&sheet), &t, 4, &env).unwrap(); + assert_eq!(get(&s4, "color"), "green"); + assert_eq!(get(&s4, "font-weight"), "700"); + assert_eq!(get(&s4, "opacity"), ".5"); + assert_eq!(get(&s4, "text-decoration"), "line-through"); + + // item 5: last-child; :not(.skip) applies + let s5 = compute(std::slice::from_ref(&sheet), &t, 5, &env).unwrap(); + assert_eq!(get(&s5, "color"), "#ff0"); + assert_eq!(get(&s5, "cursor"), "pointer"); +} + +#[test] +fn state_pseudos_match() { + let css = r" + .btn { color: gray } + .btn:hover { color: white } + .btn:active { color: black } + .btn:focus { outline: 1px } + .btn:focus-within { border-color: cyan } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + node("app"), + { + let mut n = node("btn"); + n.classes = vec!["btn".into()]; + n.hover = true; + n + }, + { + let mut n = node("btn"); + n.classes = vec!["btn".into()]; + n.active = true; + n.focus = true; + n + }, + ], + vec![NO_PARENT, 0, 0], + ); + let env = Environment::default(); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + "color" + ), + "#fff" + ); + let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + assert_eq!(get(&s2, "color"), "#000"); + assert_eq!(get(&s2, "outline"), "1px"); + // app has no focus; focus-within on ancestors of focused node — app is ancestor, but we only computed leaf nodes; focus-within on btn itself also true + assert_eq!(get(&s2, "border-color"), "#0ff"); +} + +#[test] +fn cascades_by_origin_layer_specificity_order() { + // One shared IR owner: every stylesheet borrows its parsed selectors + // from the same `CompiledStylesheet` list (the borrow of a + // `SelectorList` is invariant, so multi-sheet calls need one owner). + let mut irs = Vec::new(); + let mut push = |css: &str, origin: Origin, index: usize| { + let out = compile(css, origin, Default::default()); + assert!(out.diagnostics.is_empty(), "{:?}", out.diagnostics); + irs.push((out.sheet.expect("sheet"), index)); + }; + push(".x { color: red; padding: 1px }", Origin::Author, 0); + push(".x { color: green }", Origin::Author, 1); + push(".x { color: blue !important }", Origin::Author, 2); + push("@layer a { .x { color: cyan } }", Origin::Author, 3); + push(".x { color: white !important }", Origin::User, 4); + let sheets: Vec = irs + .iter() + .map(|(ir, index)| Stylesheet::from_ir(ir, *index)) + .collect(); + let [author, author_later, author_important, layered, important_user] = &sheets[..] else { + panic!("five sheets"); + }; + + let t = tree( + vec![{ + let mut n = node("div"); + n.classes = vec!["x".into()]; + n + }], + vec![NO_PARENT], + ); + let env = Environment::default(); + + // Later author rule wins over earlier. + let s = compute(&[author.clone(), author_later.clone()], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "green"); + assert_eq!(get(&s, "padding"), "1px"); + + // !important beats normal even from an earlier sheet. + let s = compute(&[author.clone(), author_important.clone()], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "#00f"); + + // Unlayered beats layered. + let s = compute(&[author.clone(), layered.clone()], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "red"); + + // User !important beats author !important. + let s = compute(&[author_important.clone(), important_user.clone()], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "#fff"); + + // Specificity: #id beats .class. + let specific_case = stylesheet("#y { color: purple } .x { color: orange }"); + let specific = specific_case.sheet(); + let t2 = tree( + vec![{ + let mut n = node("div"); + n.id = Some("y".into()); + n.classes = vec!["x".into()]; + n + }], + vec![NO_PARENT], + ); + let s = compute(&[specific], &t2, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "purple"); +} + +#[test] +fn inherits_and_resolves_custom_properties() { + let css = r" + :root { + --brand: oklch(70% .2 40); + --gap: 8px; + color: var(--brand); + } + .box { border-color: var(--brand); margin: var(--gap) } + .box { --gap: 4px } + .leaf { border-color: inherit } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + node("root"), // 0 + { + let mut n = node("box"); + n.classes = vec!["box".into()]; + n + }, // 1 + { + let mut n = node("leaf"); + n.classes = vec!["leaf".into()]; + n + }, // 2 + ], + vec![NO_PARENT, 0, 1], + ); + let env = Environment::default(); + + let s0 = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&s0, "color"), "oklch(70% .2 40)"); + assert_eq!(get(&s0, "--brand"), "oklch(70% .2 40)"); + + // color inherits from root; --gap overridden locally; border-color via var + let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s1, "color"), "oklch(70% .2 40)"); + assert_eq!(get(&s1, "border-color"), "oklch(70% .2 40)"); + assert_eq!(get(&s1, "margin"), "4px"); + assert_eq!(get(&s1, "--gap"), "4px"); + + // explicit inherit keyword pulls parent's border-color + let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + assert_eq!(get(&s2, "border-color"), "oklch(70% .2 40)"); + assert_eq!(get(&s2, "--gap"), "4px"); +} + +#[test] +fn var_fallbacks_and_cycles() { + let css = r" + .a { color: var(--missing, red) } + .b { --x: var(--y); --y: var(--x); color: var(--x) } + .c { color: var(--missing) } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + { + let mut n = node("d"); + n.classes = vec!["a".into()]; + n + }, + { + let mut n = node("d"); + n.classes = vec!["b".into()]; + n + }, + { + let mut n = node("d"); + n.classes = vec!["c".into()]; + n + }, + ], + vec![NO_PARENT, NO_PARENT, NO_PARENT], + ); + let env = Environment::default(); + assert_eq!( + get( + &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + "color" + ), + "red" + ); + // Cycle → declaration dropped. + assert!( + compute(std::slice::from_ref(&sheet), &t, 1, &env) + .unwrap() + .get("color") + .is_none() + ); + // Missing without fallback → dropped. + assert!( + compute(std::slice::from_ref(&sheet), &t, 2, &env) + .unwrap() + .get("color") + .is_none() + ); +} + +#[test] +fn evaluates_media_container_supports() { + let css = r" + @media (min-width: 400px) { .m { color: red } } + @media (min-width: 1000px) { .m { color: green } } + @media (prefers-color-scheme: dark) { .m { color: white } } + @container panel (inline-size > 300px) { .c { color: blue } } + @supports (color: oklch(70% .2 40)) { .s { color: oklch(70% .2 40) } } + @supports (bogus-property: 1px) { .s { color: black } } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + { + let mut n = node("app"); + n.width = Some(500.0); + n.height = Some(100.0); + n + }, // container + { + let mut n = node("m"); + n.classes = vec!["m".into()]; + n + }, + { + let mut n = node("c"); + n.classes = vec!["c".into()]; + n + }, + { + let mut n = node("s"); + n.classes = vec!["s".into()]; + n + }, + ], + vec![NO_PARENT, 0, 0, 0], + ); + let env = Environment { + viewport_width: Some(600.0), + viewport_height: Some(800.0), + color_scheme: bombshell_css_core::ColorScheme::Dark, + ..Default::default() + }; + // Both the 400px rule and the dark-scheme rule match; the dark rule is + // later in source order, so it wins. + let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s1, "color"), "#fff"); + + // With a light scheme only the viewport rule matches. + let light = Environment { + viewport_width: Some(600.0), + viewport_height: Some(800.0), + ..Default::default() + }; + let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &light).unwrap(); + assert_eq!(get(&s1, "color"), "red"); + + let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + assert!(s2.get("color").is_none(), "no container ancestor yet"); + + let s3 = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + assert_eq!(get(&s3, "color"), "oklch(70% .2 40)"); +} + +#[test] +fn container_queries_evaluate_against_ancestors() { + let css = r" + .panel { + container-type: inline-size; + container-name: panel; + } + @container panel (inline-size > 300px) { .card { color: blue } } + @container (inline-size <= 300px) { .card { color: red } } + @container panel (width > 600px) { .card { font-weight: bold } } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![ + { + let mut n = node("panel"); + n.classes = vec!["panel".into()]; + n.width = Some(400.0); + n + }, + { + let mut n = node("card"); + n.classes = vec!["card".into()]; + n + }, + ], + vec![NO_PARENT, 0], + ); + let env = Environment::default(); + let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s, "color"), "#00f"); + assert!(s.get("font-weight").is_none()); + + // Small container + let t2 = tree( + vec![ + { + let mut n = node("panel"); + n.classes = vec!["panel".into()]; + n.width = Some(200.0); + n + }, + { + let mut n = node("card"); + n.classes = vec!["card".into()]; + n + }, + ], + vec![NO_PARENT, 0], + ); + let s = compute(std::slice::from_ref(&sheet), &t2, 1, &env).unwrap(); + assert_eq!(get(&s, "color"), "red"); +} + +#[test] +fn dark_scheme_and_state_via_nesting() { + let css = r" + .btn { + color: black; + &:hover { color: white } + @media (prefers-color-scheme: dark) { + & { color: light-dark(white, black) } + } + } + "; + let sheet = stylesheet(css); + let sheet = sheet.sheet(); + let t = tree( + vec![{ + let mut n = node("btn"); + n.classes = vec!["btn".into()]; + n.hover = true; + n + }], + vec![NO_PARENT], + ); + let env = Environment { + color_scheme: bombshell_css_core::ColorScheme::Dark, + ..Default::default() + }; + let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + // :hover and dark-mode rules both match; dark rule is later in source order. + assert_eq!(get(&s, "color"), "#fff"); +} diff --git a/experiments/css/crates/css-wasm/Cargo.toml b/experiments/css/crates/css-wasm/Cargo.toml new file mode 100644 index 0000000..5b9766a --- /dev/null +++ b/experiments/css/crates/css-wasm/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "bombshell-css-wasm" +version = "0.1.0" +description = "WIT component adapter for the Bombshell CSS engine" +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[lib] +crate-type = ["cdylib"] + +[dependencies] +bombshell-css-core = { workspace = true } +bombshell-css-compiler = { workspace = true } +bombshell-css-runtime = { workspace = true } +wit-bindgen = { workspace = true } diff --git a/experiments/css/crates/css-wasm/src/lib.rs b/experiments/css/crates/css-wasm/src/lib.rs new file mode 100644 index 0000000..a0d5b75 --- /dev/null +++ b/experiments/css/crates/css-wasm/src/lib.rs @@ -0,0 +1,232 @@ +//! WIT component adapter: the only public ABI of the CSS pipeline. +//! +//! Hosts receive compiled IR as opaque bytes and evaluate computed styles +//! against a flat tree snapshot, keeping the linear-memory contract free of +//! Rust or JavaScript object layouts. + +/// The component glue only makes sense for the wasm component target; the +/// crate is a `cdylib`, and an empty dylib on native hosts keeps +/// `cargo build --workspace` runnable everywhere. +#[cfg(target_arch = "wasm32")] +mod guest { + wit_bindgen::generate!({ + path: "../../wit/css.wit", + }); + + use std::cell::RefCell; + + use bombshell::css::types::{ + ColorScheme as WitColorScheme, Diagnostic as WitDiagnostic, Environment as WitEnvironment, + Node as WitNode, NodeState, Origin as WitOrigin, Severity as WitSeverity, + }; + use bombshell_css_compiler::{CompileOptions, compile}; + use bombshell_css_core::{ + ColorScheme, CompiledStylesheet, Diagnostic, Environment, NodeSnapshot, Origin, Severity, + TreeSnapshot, decode_ir, encode_ir, + }; + use bombshell_css_runtime::Stylesheet; + use exports::bombshell::css::stylesheet::CompiledSheet; + use exports::bombshell::css::stylesheet::Guest; + + struct Css; + + impl Guest for Css { + fn compile(css: String, origin: WitOrigin) -> CompiledSheet { + let origin = to_origin(origin); + let output = compile(&css, origin, CompileOptions::default()); + let ir = output.sheet.as_ref().map(encode_ir); + CompiledSheet { + ir, + diagnostics: to_diagnostics(output.diagnostics), + } + } + + fn prepare(ir: Vec) -> Option { + // Ownership of `ir` transfers to the module (zero-copy lift); + // decode, then leak the decoded IR so the parsed stylesheet can + // borrow from it for the lifetime of the handle. + let Ok(decoded) = decode_ir(&ir) else { + return None; + }; + let sheet_ir: &'static CompiledStylesheet = Box::leak(Box::new(decoded)); + let sheet = Stylesheet::from_ir(sheet_ir, 0); + + SHEETS.with(|cell| { + let mut slots = cell.borrow_mut(); + let index = match slots.iter().position(|slot| slot.is_none()) { + Some(index) => index, + None => { + slots.push(None); + slots.len() - 1 + } + }; + slots[index] = Some(SheetSlot { + ir: sheet_ir, + sheet, + }); + Some(index as u32) + }) + } + + fn compute( + sheet: u32, + nodes: Vec, + parents: Vec, + target: u32, + env: WitEnvironment, + ) -> Vec<(String, String)> { + let Ok(tree) = to_tree(nodes, parents) else { + return Vec::new(); + }; + let Ok(target) = usize::try_from(target) else { + return Vec::new(); + }; + let environment = to_environment(env); + + SHEETS.with(|cell| -> Vec<(String, String)> { + let slots = cell.borrow(); + let Some(entry) = slots.get(sheet as usize).and_then(|slot| slot.as_ref()) else { + return Vec::new(); + }; + let style = bombshell_css_runtime::compute( + std::slice::from_ref(&entry.sheet), + &tree, + target, + &environment, + ); + // Names borrow the IR and values borrow until a var() + // substitution; the canonical-ABI lowering needs owned text. + style + .map(|style| { + style + .declarations + .into_iter() + .map(|(name, value)| (String::from(name), value.into_owned())) + .collect() + }) + .unwrap_or_default() + }) + } + + fn compute_tree( + sheet: u32, + nodes: Vec, + parents: Vec, + env: WitEnvironment, + ) -> Vec> { + let Ok(tree) = to_tree(nodes, parents) else { + return Vec::new(); + }; + let environment = to_environment(env); + + SHEETS.with(|cell| -> Vec> { + let slots = cell.borrow(); + let Some(entry) = slots.get(sheet as usize).and_then(|slot| slot.as_ref()) else { + return Vec::new(); + }; + bombshell_css_runtime::cascade::compute_tree( + std::slice::from_ref(&entry.sheet), + &tree, + &environment, + &[], + ) + .into_iter() + .map(|style| { + style + .map(|style| { + style + .declarations + .into_iter() + .map(|(name, value)| (String::from(name), value.into_owned())) + .collect() + }) + .unwrap_or_default() + }) + .collect() + }) + } + + fn release(sheet: u32) { + SHEETS.with(|cell| { + let mut slots = cell.borrow_mut(); + if let Some(entry) = slots.get_mut(sheet as usize).and_then(|slot| slot.take()) { + // The parsed stylesheet borrows from the leaked IR; drop + // it before freeing the IR so the borrow is gone first. + let SheetSlot { ir, sheet } = entry; + drop(sheet); + // SAFETY: `ir` came from `Box::leak` in `prepare`, and + // the borrowing stylesheet was dropped above. + drop(unsafe { Box::from_raw(ir as *const CompiledStylesheet as *mut CompiledStylesheet) }); + } + }); + } + } + + struct SheetSlot { + /// The leaked decoded IR, kept so `release` can free it. + ir: &'static CompiledStylesheet, + sheet: Stylesheet<'static>, + } + + thread_local! { + static SHEETS: RefCell>> = const { RefCell::new(Vec::new()) }; + } + + fn to_origin(origin: WitOrigin) -> Origin { + match origin { + WitOrigin::UserAgent => Origin::UserAgent, + WitOrigin::User => Origin::User, + WitOrigin::Author => Origin::Author, + } + } + + fn to_diagnostics(diagnostics: Vec) -> Vec { + diagnostics + .into_iter() + .map(|d| WitDiagnostic { + severity: match d.severity { + Severity::Error => WitSeverity::Error, + Severity::Warning => WitSeverity::Warning, + }, + code: d.code, + message: d.message, + }) + .collect() + } + + fn to_tree(nodes: Vec, parents: Vec) -> Result { + if nodes.len() != parents.len() { + return Err(()); + } + let nodes = nodes + .into_iter() + .map(|n| NodeSnapshot { + tag: n.tag, + id: n.id, + classes: n.classes, + attributes: n.attributes, + focus: n.state.contains(NodeState::FOCUS), + hover: n.state.contains(NodeState::HOVER), + active: n.state.contains(NodeState::ACTIVE), + width: n.width, + height: n.height, + }) + .collect(); + Ok(TreeSnapshot { nodes, parents }) + } + + fn to_environment(env: WitEnvironment) -> Environment { + Environment { + viewport_width: env.viewport_width, + viewport_height: env.viewport_height, + color_scheme: match env.color_scheme { + WitColorScheme::Light => ColorScheme::Light, + WitColorScheme::Dark => ColorScheme::Dark, + }, + reduced_motion: env.reduced_motion, + resolution: env.resolution, + } + } + + export!(Css); +} diff --git a/experiments/css/demo/README.md b/experiments/css/demo/README.md new file mode 100644 index 0000000..7cd9949 --- /dev/null +++ b/experiments/css/demo/README.md @@ -0,0 +1,35 @@ +# Demo + +A minimal browser demo that drives the full pipeline through the raw WIT +canonical ABI of `bombshell-css-wasm`: + +1. compile the editable stylesheet (lexbor front-end → typed IR), +2. decode the IR with a small postcard reader, +3. resolve computed styles for a small tree snapshot + (container queries, `@media`, nesting, custom properties), +4. display the compiled rules and the computed declarations. + +`abi.js` is the interesting part: it implements the component-model +canonical ABI by hand (string/list encoding, the `compiled-sheet` record, +the 56-byte `node` record) plus a postcard reader for IR v2. It follows the +wit-bindgen allocation protocol: host inputs are allocated with +`cabi_realloc` and transfer ownership to the module (never freed +host-side), and the callee-owned return areas are released with the +`cabi_post_*` hooks after reading. The editor drives `compile` per +keystroke, `prepare` once, and `compute-tree` per render — one canonical-ABI +crossing per frame for the whole tree. + +Build and run: + +```sh +./demo/build.sh # nightly + build-std + wasm-opt size pass + +node demo/smoke.mjs # ABI smoke test without a browser (checks the + # frame loop leaks nothing) +node demo/bench.mjs # compile-throughput benchmark +python3 -m http.server 8080 # then open http://localhost:8080/demo/ +``` + +The optimized artifact is git-ignored; build it locally as shown above. The +build also encodes it into `wasm.ts` at the repo root — the bundle-friendly +artifact hosts import instead of the binary. diff --git a/experiments/css/demo/abi.js b/experiments/css/demo/abi.js new file mode 100644 index 0000000..cc5873d --- /dev/null +++ b/experiments/css/demo/abi.js @@ -0,0 +1,478 @@ +// Canonical ABI bindings for the bombshell:css component (wasm32). +// +// All in-memory layouts follow the component-model canonical ABI. Strings +// and lists are (ptr, len) pairs pointing at wasm memory; string bytes are +// copied into linear memory via `cabi_realloc`. + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +export const NO_PARENT = 0xffffffff; + +export async function loadCssComponent(source) { + const bytes = source instanceof Uint8Array ? source : new Uint8Array(await source.arrayBuffer()); + const instance = (await WebAssembly.instantiate(bytes, {})).instance; + return createComponent(instance); +} + +export function loadCssComponentFromModule(module) { + return createComponent(new WebAssembly.Instance(module, {})); +} + +function createComponent(instance) { + const exports = instance.exports; + let memory = new Uint8Array(exports.memory.buffer); + let view = new DataView(exports.memory.buffer); + const realloc = exports.cabi_realloc; + const compileFn = exports['bombshell:css/stylesheet@0.3.0#compile']; + const computeFn = exports['bombshell:css/stylesheet@0.3.0#compute']; + const prepareFn = exports['bombshell:css/stylesheet@0.3.0#prepare']; + const releaseFn = exports['bombshell:css/stylesheet@0.3.0#release']; + const computeTreeFn = exports['bombshell:css/stylesheet@0.3.0#compute-tree']; + const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree']; + const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compile']; + const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute']; + function refresh() { + memory = new Uint8Array(exports.memory.buffer); + view = new DataView(exports.memory.buffer); + } + + // Host allocations use `cabi_realloc`: wit-bindgen lifts string/list + // arguments zero-copy (it takes ownership of the buffer and frees it with + // its own allocator), so inputs must come from the allocator the callee + // will free with, and must NOT be freed host-side. Callee-owned return + // areas are released by the `cabi_post_*` hooks after the host reads. + function allocate(bytes) { + const pointer = realloc(0, 0, 1, bytes.byteLength); + refresh(); + memory.set(bytes, pointer); + return pointer; + } + + function writeString(text) { + return allocate(encoder.encode(text)); + } + + function readString(pointer, length) { + return decoder.decode(memory.subarray(pointer, pointer + length)); + } + + function u32(pointer) { + return view.getUint32(pointer, true); + } + + function f32(pointer) { + return view.getFloat32(pointer, true); + } + + function stringPointer(text) { + const bytes = encoder.encode(text); + return { pointer: allocate(bytes), length: bytes.byteLength }; + } + + function stringListPointer(items) { + if (items.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = items.map(stringPointer); + const pointer = realloc(0, 0, 4, entries.length * 8); + refresh(); + entries.forEach((entry, i) => { + view.setUint32(pointer + i * 8, entry.pointer, true); + view.setUint32(pointer + i * 8 + 4, entry.length, true); + }); + return { pointer, count: entries.length }; + } + + function attributeListPointer(attributes) { + if (attributes.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = attributes.map((attribute) => ({ + name: stringPointer(attribute.name), + value: stringPointer(attribute.value), + })); + const pointer = realloc(0, 0, 4, entries.length * 16); + refresh(); + entries.forEach((entry, i) => { + const base = pointer + i * 16; + view.setUint32(base, entry.name.pointer, true); + view.setUint32(base + 4, entry.name.length, true); + view.setUint32(base + 8, entry.value.pointer, true); + view.setUint32(base + 12, entry.value.length, true); + }); + return { pointer, count: entries.length }; + } + + function compile(css, origin = 2) { + const bytes = encoder.encode(css); + const cssPointer = allocate(bytes); + const result = compileFn(cssPointer, bytes.byteLength, origin); + refresh(); + // compiled-sheet: option> (disc u8@0, ptr@4, len@8) + // + list (ptr@12, len@16) + // The option discriminant is one byte; bytes 1..3 are padding the + // canonical ABI leaves undefined, so they must not leak into the check. + const hasIr = view.getUint8(result) === 1; + const irPointer = u32(result + 4); + const irLength = u32(result + 8); + const diagnosticsPointer = u32(result + 12); + const diagnosticsCount = u32(result + 16); + const ir = hasIr ? memory.slice(irPointer, irPointer + irLength) : null; + const diagnostics = []; + for (let i = 0; i < diagnosticsCount; i++) { + // diagnostic: severity u8@0, code (ptr@4, len@8), message (ptr@12, len@16) + const base = diagnosticsPointer + i * 16; + diagnostics.push({ + severity: view.getUint8(base), + code: readString(u32(base + 4), u32(base + 8)), + message: readString(u32(base + 12), u32(base + 16)), + }); + } + // The callee-allocated return area (and the strings it points to) is + // released by the post-return hook, after the host has finished reading. + postCompile(result); + return { ir, diagnostics }; + } + + /// Decode and parse IR once; returns a handle for `compute`. + function prepare(ir) { + const irBytes = ir instanceof Uint8Array ? ir : new Uint8Array(ir); + const irPointer = allocate(irBytes); + const result = prepareFn(irPointer, irBytes.byteLength); + refresh(); + // option: disc u8@0, value@4 — discriminant is one byte; the + // canonical ABI leaves bytes 1..3 undefined, so read only the byte. + return view.getUint8(result) === 1 ? u32(result + 4) : null; + } + + /// Resolve computed styles for every node in one pass. Returns one + /// declaration list per node, index-aligned with `nodes`. + function computeTree(sheet, nodes, parents, env) { + const stride = 56; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + }); + const nodesPointer = allocate(data); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeTreeFn( + sheet, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list>: (ptr@0, len@4); each entry is + // (ptr@0, len@4) over 16-byte name/value pairs. + const outer = u32(result); + const outerCount = u32(result + 4); + const perNode = []; + for (let i = 0; i < outerCount; i++) { + const entryBase = outer + i * 8; + const pointer = u32(entryBase); + const count = u32(entryBase + 4); + const declarations = []; + for (let j = 0; j < count; j++) { + const base = pointer + j * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + perNode.push(declarations); + } + postComputeTree(result); + return perNode; + } + + function release(sheet) { + releaseFn(sheet); + } + + function compute(sheet, nodes, parents, target, env) { + // node: tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), + // classes list (ptr@20, len@24), attributes list + // (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), + // height option (disc@48, f32@52) — 56 bytes per node. + const stride = 56; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + }); + const nodesPointer = allocate(data); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeFn( + sheet, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + target, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list: (ptr@0, len@4) + const pointer = u32(result); + const count = u32(result + 4); + const declarations = []; + for (let i = 0; i < count; i++) { + // computed-property: name (ptr@0, len@4), value (ptr@8, len@12) + const base = pointer + i * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + postCompute(result); + return declarations; + } + + return { compile, prepare, compute, computeTree, release, raw: exports }; +} + +// --------------------------------------------------------------------------- +// Postcard reader for CompiledStylesheet (IR_VERSION 2) +// --------------------------------------------------------------------------- + +export function decodeIr(bytes) { + const reader = new IrReader(bytes); + return { + version: reader.varint(), + origin: reader.byte(), + rules: reader.vec(() => reader.rule()), + layerOrder: reader.vec(() => reader.stringVec()), + keyframes: reader.vec(() => ({ name: reader.string(), body: reader.string() })), + opaque: reader.vec(() => ({ text: reader.string() })), + warnings: reader.vec(() => reader.diagnostic()), + }; +} + +class IrReader { + constructor(bytes) { + this.bytes = bytes; + this.cursor = 0; + this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + } + + byte() { + return this.bytes[this.cursor++]; + } + + varint() { + let value = 0; + let shift = 0; + for (;;) { + const byte = this.bytes[this.cursor++]; + value |= (byte & 0x7f) << shift; + if ((byte & 0x80) === 0) { + break; + } + shift += 7; + } + return value >>> 0; + } + + zigzag() { + const n = this.varint(); + return (n >>> 1) ^ -(n & 1); + } + + string() { + const length = this.varint(); + const text = decoder.decode(this.bytes.subarray(this.cursor, this.cursor + length)); + this.cursor += length; + return text; + } + + stringVec() { + return this.vec(() => this.string()); + } + + vec(read) { + const count = this.varint(); + const items = []; + for (let i = 0; i < count; i++) { + items.push(read()); + } + return items; + } + + diagnostic() { + return { severity: this.byte(), code: this.string(), message: this.string() }; + } + + rule() { + return { + selector: this.string(), + specificity: this.varint(), + layer: this.option(() => this.stringVec()), + media: this.vec(() => this.mediaCondition()), + containers: this.vec(() => ({ + name: this.option(() => this.string()), + condition: this.containerCondition(), + })), + supports: this.vec(() => this.supportsCondition()), + declarations: this.vec(() => ({ + name: this.string(), + value: this.string(), + important: this.byte() === 1, + })), + order: this.varint(), + }; + } + + option(read) { + if (this.byte() === 0) { + return null; + } + return read(); + } + + mediaCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'feature', feature: this.feature() }; + case 1: return { type: 'not', inner: this.mediaCondition() }; + case 2: return { type: 'and', conditions: this.vec(() => this.mediaCondition()) }; + case 3: return { type: 'or', conditions: this.vec(() => this.mediaCondition()) }; + default: return { type: 'unknown' }; + } + } + + feature() { + const name = this.string(); + const kind = this.varint(); + switch (kind) { + case 0: return { name, kind: { type: 'plain', value: this.featureValue() } }; + case 1: return { name, kind: { type: 'boolean' } }; + case 2: return { name, kind: { type: 'range', cmp: this.varint(), value: this.featureValue() } }; + case 3: return { + name, + kind: { + type: 'interval', + start: this.featureValue(), + startCmp: this.varint(), + end: this.featureValue(), + endCmp: this.varint(), + }, + }; + default: throw new Error(`unknown feature kind ${kind}`); + } + } + + featureValue() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'px', value: this.f32() }; + case 1: return { type: 'length', value: this.f32(), unit: this.string() }; + case 2: return { type: 'number', value: this.f32() }; + case 3: return { type: 'integer', value: this.zigzag() }; + case 4: return { type: 'boolean', value: this.byte() === 1 }; + case 5: return { type: 'dppx', value: this.f32() }; + case 6: return { type: 'ratio', a: this.f32(), b: this.f32() }; + case 7: return { type: 'ident', value: this.string() }; + default: return { type: 'unknown' }; + } + } + + f32() { + const value = this.view.getFloat32(this.cursor, true); + this.cursor += 4; + return value; + } + + containerCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'size', feature: this.feature() }; + case 1: return { type: 'style', text: this.string() }; + case 2: return { type: 'not', inner: this.containerCondition() }; + case 3: return { type: 'and', conditions: this.vec(() => this.containerCondition()) }; + case 4: return { type: 'or', conditions: this.vec(() => this.containerCondition()) }; + default: return { type: 'unknown' }; + } + } + + supportsCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'declaration', name: this.string(), value: this.string(), supported: this.byte() === 1 }; + case 1: return { type: 'selector', text: this.string(), supported: this.byte() === 1 }; + case 2: return { type: 'not', inner: this.supportsCondition() }; + case 3: return { type: 'and', conditions: this.vec(() => this.supportsCondition()) }; + case 4: return { type: 'or', conditions: this.vec(() => this.supportsCondition()) }; + default: return { type: 'unknown' }; + } + } +} diff --git a/experiments/css/demo/bench.mjs b/experiments/css/demo/bench.mjs new file mode 100644 index 0000000..dbb3c72 --- /dev/null +++ b/experiments/css/demo/bench.mjs @@ -0,0 +1,145 @@ +// Benchmarks against the shipped component: compile throughput and the +// per-frame `compute` path real hosts drive. The corpus is generated +// deterministically, so numbers are comparable across machines and runs. +import { readFileSync } from "node:fs"; +import { loadCssComponent, decodeIr, NO_PARENT } from "./abi.js"; + +const component = await loadCssComponent( + readFileSync(new URL("./bombshell_css_wasm.wasm", import.meta.url)), +); + +// A synthetic-but-realistic stylesheet: 1140 top-level class rules cycling +// real property/value shapes, plus nesting, media, container, keyframes, +// and supports blocks in the mix. +const PROPS = [ + ["color", ["red", "#34a", "oklch(70% .2 40)", "rgb(10 20 30 / 50%)"]], + ["background-color", ["transparent", "light-dark(white, black)"]], + ["margin", ["0", "4px", "1.5rem", "8px 16px"]], + ["padding", ["0 auto", "2px 4px 6px"]], + ["font-size", ["12px", "1.25rem", "100%"]], + ["border-color", ["red", "color-mix(in oklab, red 40%, blue)"]], + ["width", ["auto", "100px", "50%", "min(10px, 2em)"]], + ["display", ["block", "flex", "none"]], + ["opacity", ["1", ".5"]], + ["--custom", ["var(--x, 1px)", "oklch(50% .1 200)"]], +]; +const VARIANTS = [ + (n) => `.c${n}`, + (n) => `.c${n}:hover`, + (n) => `.c${n}[data-x='${n}']`, + (n) => `.c${n} > .child`, + (n) => `:is(.c${n}, .c${n + 1})`, + (n) => `.c${n}:nth-child(2n + 1)`, +]; +let css = ""; +let n = 0; +while (n < 1140) { + const [prop, values] = PROPS[n % PROPS.length]; + const value = values[(n / PROPS.length) % values.length | 0]; + css += `${VARIANTS[n % VARIANTS.length](n)} { ${prop}: ${value} }\n`; + n++; +} +css += `@media (min-width: 24em) { ${VARIANTS[0](n)} { ${PROPS[0][0]}: blue } ${VARIANTS[1](n + 1)} { ${PROPS[2][0]}: 2px } }\n`; +n += 2; +css += `@container panel (inline-size > 20em) { .c${n} { ${PROPS[5][0]}: green } }\n`; +n += 1; +css += `.panel { container-type: inline-size; container-name: panel }\n`; +css += `.card { padding: 4px; & > .title { font-weight: bold } .body { margin: 0 } }\n`; +css += `@layer base { .c${n} { ${PROPS[0][0]}: red } .c${n + 1} { ${PROPS[3][0]}: 1px } }\n`; +n += 2; +css += `@supports (color: oklch(50% .1 200)) { .c${n} { ${PROPS[0][0]}: oklch(50% .1 200) } }\n`; +n += 1; +css += `@keyframes pulse { 0% { opacity: 1 } 100% { opacity: 0 } }\n`; +css += `.c${n} { ${PROPS[8][0]}: var(--x, .4) }\n`; + +// --------------------------------------------------------------------------- +// Compile throughput +// --------------------------------------------------------------------------- + +const run = () => component.compile(css, "author"); + +for (let i = 0; i < 5; i++) run(); + +const N = 50; +const times = []; +let rules = 0; +for (let i = 0; i < N; i++) { + const start = performance.now(); + const result = run(); + times.push(performance.now() - start); + if (i === 0) { + const ir = decodeIr(result.ir); + rules = ir.rules.length; + } +} +times.sort((a, b) => a - b); +const median = times[N >> 1]; +console.log( + `compile (wasm): median ${median.toFixed(2)} ms → ${(css.length / 1024 / median * 1000).toFixed(1)} KB/s (${rules} rules)`, +); + +// --------------------------------------------------------------------------- +// Per-frame compute: a UI-shaped tree over the corpus's own class names +// --------------------------------------------------------------------------- + +const compiled = run(); +const ir = compiled.ir; +const sheet = decodeIr(ir); +const handle = component.prepare(ir); +if (handle === null) throw new Error("prepare rejected the IR"); + +// Class vocabulary from the compiled selectors, most frequent first. +const freq = new Map(); +for (const rule of sheet.rules) { + for (const match of rule.selector.matchAll(/\.([A-Za-z][\w-]*)/g)) { + freq.set(match[1], (freq.get(match[1]) ?? 0) + 1); + } +} +const classes = [...freq.entries()].sort((a, b) => b[1] - a[1]).map(([c]) => c); +if (classes.length === 0) classes.push("node"); + +// A plausible UI tree: a container panel with a grid of cards, each with a +// title and a couple of children — 1 + 4 + 4*(1+3) = 21 nodes, sizes set on +// the container so size queries can match. +const tree = [{ tag: "root", classes: [classes[0]], width: 1024, height: 768, state: 0 }]; +const parents = [NO_PARENT]; +const panel = tree.length; +tree.push({ tag: "panel", classes: [classes[1] ?? classes[0]], width: 1024, height: 768, state: 0 }); +parents.push(0); +for (let i = 0; i < 4; i++) { + const card = tree.length; + tree.push({ tag: "card", classes: [classes[i % classes.length]], state: i === 1 ? 1 : 0 }); + parents.push(panel); + for (let j = 0; j < 3; j++) { + tree.push({ tag: "text", classes: [classes[(i + j + 2) % classes.length]], state: 0 }); + parents.push(card); + } +} + +const env = { viewportWidth: 1024, viewportHeight: 768, colorScheme: "dark", reducedMotion: false, resolution: 1 }; + +// Warm. +for (const target of tree.keys()) component.compute(handle, tree, parents, target, env); + +const FRAMES = 100; +const frameTimes = []; +for (let f = 0; f < FRAMES; f++) { + const start = performance.now(); + component.computeTree(handle, tree, parents, env); + frameTimes.push(performance.now() - start); +} +frameTimes.sort((a, b) => a - b); +const frame = frameTimes[FRAMES >> 1]; +console.log( + `compute-tree (wasm): median ${frame.toFixed(3)} ms/frame → ${(frame * 1000 / tree.length).toFixed(1)} µs/node × ${tree.length} nodes`, +); + +// Memory stability: a frame loop must not grow linear memory. +const before = component.raw.memory.buffer.byteLength; +for (let f = 0; f < 2000; f++) { + component.computeTree(handle, tree, parents, env); +} +const growth = component.raw.memory.buffer.byteLength - before; +console.log(`compute loop: 2000 frames, memory grew ${growth} bytes`); +if (growth !== 0) throw new Error(`per-frame leak: ${growth} bytes`); +component.release(handle); diff --git a/experiments/css/demo/build.sh b/experiments/css/demo/build.sh new file mode 100755 index 0000000..b78d474 --- /dev/null +++ b/experiments/css/demo/build.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Build the shipped wasm component. Size-minimal: the nightly toolchain is +# required for the std rebuild (panic_immediate_abort strips panic payloads) +# and for the rustc location-detail/fmt-debug flags that keep panic location +# strings and Debug impls out of linear memory. +set -e + +FLAGS="-Zunstable-options -Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none" + +RUSTFLAGS="$FLAGS" cargo +nightly build -p bombshell-css-wasm --target wasm32-unknown-unknown --release \ + -Z build-std=std,panic_abort + +# Feature set must match what the module uses: no GC (binaryen's bottom-type +# encoding with GC enabled is not parseable by V8). A second -Oz pass shaves +# the last few hundred bytes. +wasm-opt target/wasm32-unknown-unknown/release/bombshell_css_wasm.wasm \ + -Oz \ + --enable-bulk-memory --enable-reference-types --enable-nontrapping-float-to-int \ + --enable-sign-ext --enable-mutable-globals --enable-multivalue --enable-tail-call \ + --strip-debug --strip-producers \ + -o demo/bombshell_css_wasm.wasm + +wasm-opt demo/bombshell_css_wasm.wasm \ + -Oz \ + --enable-bulk-memory --enable-reference-types --enable-nontrapping-float-to-int \ + --enable-sign-ext --enable-mutable-globals --enable-multivalue --enable-tail-call \ + -o demo/bombshell_css_wasm.wasm + +stat -f "%z bytes" demo/bombshell_css_wasm.wasm + +# Encode the optimized module into the bundle-friendly artifact: a +# self-contained wasm.ts exporting the compiled module (same scheme as the +# tty repo). Needs deno. +deno run --allow-read --allow-write tasks/bundle-wasm.ts diff --git a/experiments/css/demo/index.html b/experiments/css/demo/index.html new file mode 100644 index 0000000..f4092a0 --- /dev/null +++ b/experiments/css/demo/index.html @@ -0,0 +1,61 @@ + + + + + + Bombshell CSS + + + +
+

Bombshell CSS

+

+ lightningcss front-end → typed IR → parcel_selectors runtime, running + as a freestanding wasm module. +

+
+
+
+ + + + + … +
+
+ +

+      
+
+ + + diff --git a/experiments/css/demo/main.js b/experiments/css/demo/main.js new file mode 100644 index 0000000..47d0ef1 --- /dev/null +++ b/experiments/css/demo/main.js @@ -0,0 +1,89 @@ +import { loadCssComponent, decodeIr, NO_PARENT } from './abi.js'; + +const source = document.querySelector('#source'); +const output = document.querySelector('#output'); +const status = document.querySelector('#status'); +const widthInput = document.querySelector('#viewport-width'); + +const component = await loadCssComponent(await (await fetch('./bombshell_css_wasm.wasm')).blob()); +let sheet_handle = null; + +// The demo tree: a panel container with two cards, one focused. +const nodes = [ + { tag: 'root', classes: ['root', 'panel'], width: 400, height: null, state: 0 }, + { tag: 'card', classes: ['card'], state: 0 }, + { tag: 'card', classes: ['card', 'special'], state: 1 }, // focused + { tag: 'title', classes: ['title'] }, + { tag: 'title', classes: ['title'] }, +]; +const parents = [NO_PARENT, 0, 0, 1, 2]; +const tags = ['root', 'card', 'card.special (focus)', 'title', 'title']; + +function format(condition) { + if (!condition || condition.type === 'unknown') return 'unknown'; + switch (condition.type) { + case 'feature': return `${condition.feature.name}`; + case 'not': return `not ${format(condition.inner)}`; + case 'and': return condition.conditions.map(format).join(' and '); + case 'or': return condition.conditions.map(format).join(' or '); + case 'size': return `${condition.feature.name}`; + case 'style': return condition.text; + case 'declaration': return `${condition.name}: ${condition.value}`; + case 'selector': return condition.text; + default: return condition.type; + } +} + +function run() { + try { + const { ir, diagnostics } = component.compile(source.value, 2); + const lines = []; + for (const diagnostic of diagnostics) { + lines.push(`${diagnostic.severity === 0 ? 'error' : 'warning'} [${diagnostic.code}] ${diagnostic.message}`); + } + + if (ir) { + const sheet = decodeIr(ir); + if (sheet_handle !== null) component.release(sheet_handle); + sheet_handle = component.prepare(ir); + if (sheet_handle === null) throw new Error('prepare rejected the IR'); + lines.push(`IR v${sheet.version} — ${ir.byteLength} bytes, ${sheet.rules.length} rules`); + lines.push(''); + const env = { + viewportWidth: Number(widthInput.value) || null, + viewportHeight: 300, + colorScheme: 'dark', + reducedMotion: false, + resolution: 1, + }; + const computedPerNode = component.computeTree(sheet_handle, nodes, parents, env); + nodes.forEach((node, index) => { + lines.push(`${tags[index]}:`); + for (const [name, value] of computedPerNode[index]) { + lines.push(` ${name}: ${value}`); + } + lines.push(''); + }); + lines.push('compiled rules:'); + for (const rule of sheet.rules) { + const conditions = [ + ...rule.media.map((m) => `@media ${format(m)}`), + ...rule.containers.map((c) => `@container ${c.name ? c.name + ' ' : ''}${format(c.condition)}`), + ...rule.supports.map((s) => `@supports ${format(s)}`), + ]; + const prefix = conditions.length ? `${conditions.join(' AND ')} » ` : ''; + const layer = rule.layer ? `@layer ${rule.layer.join('.')} » ` : ''; + lines.push(` ${prefix}${layer}${rule.selector}`); + } + } + output.textContent = lines.join('\n'); + status.value = ir ? 'ok' : 'compile failed'; + } catch (error) { + status.value = 'error'; + output.textContent = error instanceof Error ? error.stack : String(error); + } +} + +source.addEventListener('input', run); +widthInput.addEventListener('input', run); +run(); diff --git a/experiments/css/demo/smoke.mjs b/experiments/css/demo/smoke.mjs new file mode 100644 index 0000000..1ac6ce9 --- /dev/null +++ b/experiments/css/demo/smoke.mjs @@ -0,0 +1,140 @@ +// Smoke test: drive the raw canonical ABI from Node. +// Usage: node demo/smoke.mjs [path-to-wasm] + +import { readFileSync } from 'node:fs'; +import { loadCssComponent, decodeIr, NO_PARENT } from './abi.js'; + +const path = process.argv[2] ?? new URL('./bombshell_css_wasm.wasm', import.meta.url).pathname; +const component = await loadCssComponent(readFileSync(path)); + +const css = ` + :root { --brand: oklch(70% .2 40); color: var(--brand) } + .panel { container-type: inline-size; container-name: panel } + .card { padding: 4px; color: light-dark(white, black) } + .card { & > .title { font-weight: bold } } + @media (min-width: 24em) { + @container panel (inline-size > 20em) { + .card { border-color: var(--brand) } + } + } +`; + +const { ir, diagnostics } = component.compile(css, 2); +console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); +if (!ir) { + throw new Error('compile returned no IR'); +} +console.log('ir bytes:', ir.byteLength); +const sheet_handle = component.prepare(ir); +if (sheet_handle === null) { + throw new Error('prepare rejected the IR'); +} + +const sheet = decodeIr(ir); +console.log('ir version:', sheet.version); +for (const rule of sheet.rules) { + console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); +} + +const tree = [ + { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, // container + { tag: 'card', classes: ['card'] }, + { tag: 'title', classes: ['title'] }, +]; +const parents = [NO_PARENT, 0, 1]; + +// Node 0 as a named container via container-type comes from computed styles; +// this fixture relies on the width. Compute each node with a dark viewport. +const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; +for (const target of [0, 1, 2]) { + const computed = component.compute(sheet_handle, tree, parents, target, env); + console.log(`node ${target}:`, JSON.stringify(computed)); +} + +// Frame loop: `compute` runs per animation frame in real hosts, so its +// memory must not grow over time. The component caches the parsed sheet for +// identical IR bytes; a growing trace means the cache is leaking. +function memoryPages() { + return component.raw.memory.buffer.byteLength; +} +// The tree API must agree with the per-node API. +const perNode = []; +for (const target of tree.keys()) { + perNode.push(component.compute(sheet_handle, tree, parents, target, env)); +} +const whole = component.computeTree(sheet_handle, tree, parents, env); +if (JSON.stringify(perNode) !== JSON.stringify(whole)) { + throw new Error('computeTree disagrees with compute'); +} + +// Re-prepare + release exercises the slot lifecycle before the loop. +const other = component.compile('.x { color: blue }', 2); +const other_handle = component.prepare(other.ir); +component.compute(other_handle, tree, parents, 2, env); +component.release(other_handle); +for (let i = 0; i < 500; i++) { + component.compute(sheet_handle, tree, parents, 2, env); +} +if (memoryPages() !== memoryPages()) throw new Error('unstable'); +const pagesAfterFrames = memoryPages(); +for (let i = 0; i < 500; i++) { + for (const target of tree.keys()) { + component.compute(sheet_handle, tree, parents, target, env); + } + component.computeTree(sheet_handle, tree, parents, env); +} +const growth = memoryPages() - pagesAfterFrames; +console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); +if (growth > 0) { + throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); +} + +// Option-returning calls after compute-tree: the shared return area holds a +// pointer from compute-tree's lowering, so the option discriminant's padding +// bytes are no longer zero. Reading the disc as anything wider than one byte +// misreads these results as absent. +const hot = component.compile('.card { color: #fff }', 2); +if (hot.ir === null) { + throw new Error('compile after compute-tree reported no IR'); +} +const hotHandle = component.prepare(hot.ir); +if (hotHandle === null) { + throw new Error('prepare after compute-tree rejected valid IR'); +} +component.release(hotHandle); + +// The rest of the poison matrix: re-preparing the ORIGINAL IR bytes… +const rePrepare = component.prepare(ir); +if (rePrepare === null) { + throw new Error('re-prepare of the existing IR rejected valid bytes'); +} +// …and computing on a sheet adopted after compute-tree. +const adopted = component.compile('.card { color: #0f0 }', 2); +if (adopted.ir === null) { + throw new Error('compile after compute-tree reported no IR'); +} +const adoptedHandle = component.prepare(adopted.ir); +if (adoptedHandle === null) { + throw new Error('prepare after compute-tree rejected valid IR'); +} +const afterPoison = component.computeTree(adoptedHandle, tree, parents, env); +if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { + throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); +} +component.release(rePrepare); +component.release(adoptedHandle); + +// Diagnostics surface through the ABI: an invalid color warns and the rest +// of the rule recovers. +const warned = component.compile('.card { color: notacolor; padding: 4px }', 2); +if ( + warned.ir === null || + !warned.diagnostics.some( + (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), + ) +) { + throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); +} + +component.release(sheet_handle); +console.log('smoke ok'); diff --git a/experiments/css/demo/styles.css b/experiments/css/demo/styles.css new file mode 100644 index 0000000..b905a24 --- /dev/null +++ b/experiments/css/demo/styles.css @@ -0,0 +1,109 @@ +:root { + color-scheme: dark; + font-family: ui-monospace, SFMono-Regular, Menlo, monospace; + background: #111; + color: #eee; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + min-height: 100vh; + background: radial-gradient(circle at top right, #29352e, #111 48%); +} + +header { + max-width: 1100px; + margin: 0 auto; + padding: 48px 24px 8px; +} + +h1 { + margin: 0 0 8px; + font-size: 1.6rem; + letter-spacing: -0.02em; +} + +header p { + margin: 0; + max-width: 640px; + color: #a8b0aa; + line-height: 1.6; +} + +main { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; + max-width: 1100px; + margin: 24px auto 64px; + padding: 0 24px; +} + +.editor, +.result { + display: grid; + align-content: start; + gap: 10px; + min-width: 0; +} + +label { + color: #93d4a4; + font-size: 0.75rem; + letter-spacing: 0.12em; + text-transform: uppercase; +} + +textarea, +pre, +input, +output { + font: inherit; + line-height: 1.55; +} + +textarea, +pre { + width: 100%; + min-height: 360px; + margin: 0; + padding: 18px; + overflow: auto; + border: 1px solid #334139; + border-radius: 8px; + background: #151b17; + color: #e8f3e9; +} + +textarea { + resize: vertical; + outline: none; +} + +textarea:focus { + border-color: #93d4a4; + box-shadow: 0 0 0 3px #93d4a422; +} + +input { + width: 140px; + padding: 6px 10px; + border: 1px solid #334139; + border-radius: 6px; + background: #151b17; + color: #e8f3e9; +} + +output { + color: #a8b0aa; +} + +@media (max-width: 720px) { + main { + grid-template-columns: 1fr; + } +} diff --git a/experiments/css/tasks/bundle-wasm.ts b/experiments/css/tasks/bundle-wasm.ts new file mode 100644 index 0000000..18b8d45 --- /dev/null +++ b/experiments/css/tasks/bundle-wasm.ts @@ -0,0 +1,63 @@ +import { brotliCompressSync, constants } from "node:zlib"; + +const Z85 = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; + +function encodeZ85(data: Uint8Array): string { + let padLen = (4 - (data.length % 4)) % 4; + let src = data; + if (padLen > 0) { + src = new Uint8Array(data.length + padLen); + src.set(data); + } + let out: string[] = []; + for (let i = 0; i < src.length; i += 4) { + let v = src[i] * 16777216 + + src[i + 1] * 65536 + + src[i + 2] * 256 + + src[i + 3]; + out.push( + Z85[Math.floor(v / 52200625)], + Z85[Math.floor(v / 614125) % 85], + Z85[Math.floor(v / 7225) % 85], + Z85[Math.floor(v / 85) % 85], + Z85[v % 85], + ); + } + return out.join(""); +} + +const wasm = await Deno.readFile("demo/bombshell_css_wasm.wasm"); + +const compressed = new Uint8Array( + brotliCompressSync(wasm, { + params: { + [constants.BROTLI_PARAM_QUALITY]: 11, + [constants.BROTLI_PARAM_SIZE_HINT]: wasm.length, + [constants.BROTLI_PARAM_LGWIN]: 24, + }, + }), +); + +const z85 = encodeZ85(compressed); + +// The decoder receives the pre-padding module length, so the zero bytes the +// Z85 alignment pad contributes never reach WebAssembly.compile. +// Decoding uses division instead of >>> to avoid 32-bit truncation on +// values near 0xFFFFFFFF. +const source = `import{brotliDecompressSync}from"node:zlib"; +const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; +const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; +// the table lookups are non-null for well-formed z85 input; the bounds +// checks below handle a short tail, so the assertions carry no risk +function d(s:string,n:number){const b=new Uint8Array(n);let o=0;for(let i=0;i .title { font-weight: bold } } + @media (min-width: 24em) { + @container panel (inline-size > 20em) { + .card { border-color: var(--brand) } + } + } +`; + +const { ir, diagnostics } = component.compile(css, 2); +console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); +if (!ir) { + throw new Error('compile returned no IR'); +} +console.log('ir bytes:', ir.byteLength); +const sheet_handle = component.prepare(ir); +if (sheet_handle === null) { + throw new Error('prepare rejected the IR'); +} + +const sheet = decodeIr(ir); +console.log('ir version:', sheet.version); +for (const rule of sheet.rules) { + console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); +} + +const tree = [ + { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, + { tag: 'card', classes: ['card'] }, + { tag: 'title', classes: ['title'] }, +]; +const parents = [NO_PARENT, 0, 1]; + +const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; + +const perNode = []; +for (const target of tree.keys()) { + perNode.push(component.compute(sheet_handle, tree, parents, target, env)); +} +const whole = component.computeTree(sheet_handle, tree, parents, env); +if (JSON.stringify(perNode) !== JSON.stringify(whole)) { + throw new Error('computeTree disagrees with compute'); +} +if (!whole[1].some(([name, value]) => name === 'border-color' && value === 'oklch(70% .2 40)')) { + throw new Error(`container query did not cascade: ${JSON.stringify(whole[1])}`); +} + +const other = component.compile('.x { color: blue }', 2); +const other_handle = component.prepare(other.ir); +component.compute(other_handle, tree, parents, 2, env); +component.release(other_handle); + +function memoryPages() { + return component.raw.memory.buffer.byteLength; +} +for (let i = 0; i < 500; i++) { + component.compute(sheet_handle, tree, parents, 2, env); +} +if (memoryPages() !== memoryPages()) throw new Error('unstable'); +const pagesAfterFrames = memoryPages(); +for (let i = 0; i < 500; i++) { + for (const target of tree.keys()) { + component.compute(sheet_handle, tree, parents, target, env); + } + component.computeTree(sheet_handle, tree, parents, env); +} +const growth = memoryPages() - pagesAfterFrames; +console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); +if (growth > 0) { + throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); +} +component.release(sheet_handle); +console.log('bundle ok'); diff --git a/experiments/css/wit/css.wit b/experiments/css/wit/css.wit new file mode 100644 index 0000000..6c20576 --- /dev/null +++ b/experiments/css/wit/css.wit @@ -0,0 +1,125 @@ +package bombshell:css@0.3.0; + +/// Shared record types crossing the CSS boundary. +interface types { + /// Cascade origin of a stylesheet. + enum origin { + user-agent, + user, + author, + } + + enum severity { + error, + warning, + } + + record diagnostic { + severity: severity, + /// Machine-readable identifier, e.g. `parse`, `lightningcss`. + code: string, + message: string, + } + + enum color-scheme { + light, + dark, + } + + /// Interactive state flags on a node. + flags node-state { + focus, + hover, + active, + } + + /// Attribute name/value pair. + type attribute = tuple; + + /// One element of the host tree. Trees are shipped as a flat, + /// document-ordered `list` plus a parallel `list` of parent + /// indices; `0xffffffff` (u32::MAX) marks a root with no parent. Sibling + /// relationships are derived from index contiguity. + record node { + tag: string, + id: option, + classes: list, + attributes: list, + state: node-state, + /// Layout width in CSS px, if known. Satisfies container/media queries. + width: option, + /// Layout height in CSS px, if known. + height: option, + } + + /// Rendering environment consulted by `@media` evaluation. Unknown + /// dimensions are `None`; queries referencing unknown dimensions never + /// match. + record environment { + viewport-width: option, + viewport-height: option, + color-scheme: color-scheme, + /// `true` when the user prefers reduced motion. + reduced-motion: bool, + /// Device pixel ratio (1.0 default). + resolution: f32, + } + + /// Computed declaration name/value pair. + type computed-property = tuple; +} + +/// The CSS compiler/runtime boundary. +interface stylesheet { + use types.{diagnostic, origin, node, environment, computed-property}; + + /// Compiled stylesheet IR plus diagnostics. With error recovery the IR is + /// present and diagnostics may carry warnings; on fatal parse errors the + /// IR is absent. + record compiled-sheet { + ir: option>, + diagnostics: list, + } + + /// Compile CSS into versioned IR bytes. The IR is opaque to the host; + /// pass it to `prepare` to style against it. + compile: func(css: string, origin: origin) -> compiled-sheet; + + /// Decode and parse a compiled stylesheet once. Returns a handle to use + /// with `compute`; `None` when the IR is malformed or carries an + /// unsupported version. The handle stays valid until `release`; passing a + /// released or unknown handle to `compute` yields an empty result. + prepare: func(ir: list) -> option; + + /// Resolve the computed style for the node at `target`. The host supplies + /// the full flat tree snapshot (nodes in document order plus parent + /// indices) and the environment. Ancestors are cascaded automatically for + /// inheritance, custom properties, and container lookups. Designed for + /// per-frame use: the parsed stylesheet is reused across calls. + compute: func( + sheet: u32, + nodes: list, + parents: list, + target: u32, + env: environment, + ) -> list; + + /// Resolve computed styles for every node of the tree in one pass: nodes + /// are processed in document order, each node's cascade runs exactly once, + /// and descendants inherit computed values. Returns one entry per node, + /// index-aligned with `nodes`. This is the per-frame entry point. + compute-tree: func( + sheet: u32, + nodes: list, + parents: list, + env: environment, + ) -> list>; + + /// Drop a prepared stylesheet. `sheet` may then be reused by a later + /// `prepare`. + release: func(sheet: u32); +} + +world css { + export stylesheet; +} From 73e0d346a3f3283a22df626148871f7678b9d542 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:07:01 -0400 Subject: [PATCH 2/9] Trim experiments/css to the crate workspace --- experiments/css/BUGS-from-ui-bridge.md | 154 ------- experiments/css/HANDOFF-compute-poison.md | 86 ---- experiments/css/README.md | 206 ---------- experiments/css/demo/README.md | 35 -- experiments/css/demo/abi.js | 478 ---------------------- experiments/css/demo/bench.mjs | 145 ------- experiments/css/demo/build.sh | 34 -- experiments/css/demo/index.html | 61 --- experiments/css/demo/main.js | 89 ---- experiments/css/demo/smoke.mjs | 140 ------- experiments/css/demo/styles.css | 109 ----- experiments/css/tasks/bundle-wasm.ts | 63 --- experiments/css/tasks/verify-bundle.ts | 85 ---- 13 files changed, 1685 deletions(-) delete mode 100644 experiments/css/BUGS-from-ui-bridge.md delete mode 100644 experiments/css/HANDOFF-compute-poison.md delete mode 100644 experiments/css/README.md delete mode 100644 experiments/css/demo/README.md delete mode 100644 experiments/css/demo/abi.js delete mode 100644 experiments/css/demo/bench.mjs delete mode 100755 experiments/css/demo/build.sh delete mode 100644 experiments/css/demo/index.html delete mode 100644 experiments/css/demo/main.js delete mode 100644 experiments/css/demo/smoke.mjs delete mode 100644 experiments/css/demo/styles.css delete mode 100644 experiments/css/tasks/bundle-wasm.ts delete mode 100644 experiments/css/tasks/verify-bundle.ts diff --git a/experiments/css/BUGS-from-ui-bridge.md b/experiments/css/BUGS-from-ui-bridge.md deleted file mode 100644 index f3eb375..0000000 --- a/experiments/css/BUGS-from-ui-bridge.md +++ /dev/null @@ -1,154 +0,0 @@ -# Bug report: compile/compute regressions hit from the UI DOM bridge - -_From the bombshell-dev/ui spike (DOM core + CSS bridge). Written 2026-10-12. Reproed against `demo/bombshell_css_wasm.wasm` (214,025 bytes, md5 `e2848c97d90ece5153e05a096ada3a2d`) + unmodified `demo/abi.js` — the exact artifact/ABI the smoke test passes with (`node demo/smoke.mjs` → all green, 5500 computes, 0 memory growth)._ - -Two independent failures, both silently (empty `diagnostics`): - -## Bug 3 — `compute_tree` poisons subsequent `compile` calls (the big one) - -Minimal sequence, fresh process: - -```js -const ua = component.compile('tui-button { display: flex; border: 1px solid #3a3a46; cursor: pointer }', 0); -const uaSheet = ua.ir && component.prepare(ua.ir); -component.computeTree(uaSheet, nodes, parents, env); // one compute is enough -const author = component.compile('tui-text { color: red }', 2); -console.log(author.ir); // → null, diagnostics: [] -``` - -Controls (same component code paths, minus one step each): - -| sequence | second compile | -| --- | --- | -| compile → prepare → **compute_tree** → compile | **ir: null** (silent) | -| compile → prepare → compile | ir: 34 ✓ | -| compile → compile (no prepare) | ir: 34 ✓ | - -So one `compute_tree` call breaks every later `compile` — with empty diagnostics. This is why the smoke suite can't see it: it compiles once, computes many. The UI bridge compiles at runtime (hot adoption) and hits it constantly. Host-side workaround in place (fresh component re-instantiation per compile batch, `createCssComponentSync`), but the real fix belongs here — my guess is allocator/arena state (the compute-tree's lifted-list frees) that `compile`'s next call walks into, possibly related to the recent table-packing commits. - -Repro: the three-line sequence above against `demo/bombshell_css_wasm.wasm` (md5 `858bcb0c23f8881a8c87254a23418b6a`). - -## Bug 1 — single-line stylesheet compiles to NO IR - -```js -component.compile('.card { color: #fff; padding: 4px }', 2) // → { ir: null, diagnostics: [] } -component.compile('.card {\n color: #fff;\n padding: 4px;\n}', 2) // → { ir: <74 bytes>, diagnostics: [] } -``` - -Identical CSS modulo whitespace/newlines. The single-line form produces **no IR and no diagnostics** — `prepare` then rejects (`null`), and the host can't tell the user why. Looks like a token-level event from the lexbor shim being lost when the rule body ends on the same line it starts (the smoke's stylesheet — like all real stylesheets — is multiline, which is why the corpus doesn't catch it). Suggests a conformance-corpus gap: add single-line-rule cases to `css-conformance/cases/`. - -## Bug 2 — hex color declarations: dropped from computed, or NO IR, depending on digit count - -Matrix (each row a fresh process, multiline sheet `.card {\n ;\n}`, compute-tree on one `.card` node): - -| declaration | compile | computed | -| --- | --- | --- | -| `color: red` | ✓ | `color: red` present ✓ | -| `color: rgb(40 40 48)` | ✓ | present ✓ | -| `color: light-dark(white, black)` (smoke) | ✓ | `color: #fff` present ✓ | -| `background-color: #101010` | ✓ | present ✓ | -| `border-color: rgb(40 40 48)` | ✓ | present ✓ | -| `color: #fff` | ✓ | **declaration vanishes** (computed `[[]]`) | -| `color: #ff8800` | ✓ | **declaration vanishes** | -| `color: #ffffff` | **NO IR**, no diagnostics | — | -| `border-color: #333` | **NO IR**, no diagnostics | — | -| `border-color: #333333` | **NO IR**, no diagnostics | — | - -So: named/functional colors are fine; a 3-digit hex on `color` compiles but the declaration is silently dropped downstream; 6-digit hex (and 3-digit hex on `border-color`) silently produce no IR at all. `background-color: #101010` (6-digit hex) surviving while `color: #ffffff` dies suggests the canonicalizer/path differs per property — likely a span/boundary issue around the `#` hex token in the lexbor shim's event stream for the `color`/`border-color` grammar specifically. - -Guess at the culprit: the compiler's canonical value serialization for hex colors, or the runtime's declaration filter not recognizing the canonical form for those properties. Whatever it is, `diagnostics: []` on all the failures is its own bug — silent data loss. - -Also observed (lower confidence, only if useful): running several `compile()` calls back-to-back in one process, the later ones occasionally produced `ir: null` as well (seen with single-line inputs) — could be entirely explained by Bug 1; flagging in case there's per-call state involved. - -## Context: why the UI bridge cares - -The ui DOM bridge feeds `compute-tree` a flat element snapshot per frame and merges the returned declarations onto tty render properties. `color`/`border-color` are *the* properties for terminal text and borders — Bug 2 makes stylesheet-driven terminal styling impossible for plain colors, and Bug 1 makes half of real-world CSS (minified/single-line sheets) silently no-op. - -Repro script (run from `demo/`): the two snippets above; `demo/smoke.mjs` continues to pass, so any fix should be asserted by extending the corpus with: (a) single-line rules, (b) plain-hex color + border-color computed expectations. - -— the ui-side spike - ---- - -## Resolution (engine side, 2026-10-12) - -All three bugs are fixed; the "engine poison" in Bug 3 was never engine state. - -**Bug 3 (`compute_tree` "poisons" later `compile`/`prepare`): host-side ABI misread.** -The canonical ABI lowers `option<...>` results as a 1-byte discriminant followed -by 3 bytes of *undefined* padding (then the payload at +4). wit-bindgen's guest -shares one static return area across all exports, and `compute_tree` writes an -outer-list pointer there; a later `compile`/`prepare` writes its discriminant -over byte 0 and leaves the stale pointer fragments in bytes 1..3. The old host -readers (`demo/abi.js`, and the ui's `abi.ts` adapted from it) read the -discriminant as `u32` — so a fully successful guest result (`disc=1, handle=1`) -read as `0x00110201 ≠ 1` → "null", with the guest's real diagnostics never -examined. That is the entire poison matrix: fresh components work (zeroed -return area), `compute` is immune (no discriminant in its result), and the -same IR bytes work on a fresh component. Byte-level trace: after one -`compute_tree` whose outer list lives at `0x00110250`, the next `compile` -returns `01 02 11 00 …` — disc `1`, padding `02 11 00`, valid IR pointer and -length behind it. - -Fixes: `demo/abi.js` and the ui's `packages/ui/src/css/abi.ts` now read the -discriminant with `getUint8`; the ui bridge's fresh-component-reload -workaround is deleted. The engine's wasm guest is unchanged — the rebuilt -artifact is byte-identical (214,412 bytes, md5 `858bcb0c23f8881a8c87254a23418b6a`). -Regression protection: `demo/smoke.mjs` asserts the full matrix -(compile/prepare/re-prepare after compute-tree, plus a sheet adopted and -computed after the compute window), and `css-conformance` pins the -library-level property (`compile_and_prepare_survive_compute_tree`). - -Known limitations left as-is (WIT `@0.3.0` shapes, not regressions): -`prepare` has no diagnostics channel (invalid IR → `null` only), and -`compute`/`compute_tree` degrade to empty results for unknown handles — -handles are never invalidated on `release`, and a freed slot can be reused by -a later `prepare`, so a stale handle aliases the new sheet. Worth a `@0.4.0` -discussion someday; nothing here depends on it. - -**Bug 1 (single-line stylesheets produce no IR): fixed in the compiler.** -The lexbor shim's token stream lost the rule-body-end event when the closing -brace sat on the rule's first line. Corpus case `values/02-single-line.css` + -behavior test. - -**Bug 2 (hex colors dropped or NO IR): fixed in the compiler.** -Hex colors tokenize as `Hash` or `IDHash` depending on whether the payload -reads as an identifier; only `Hash` was recognized, and digit-count -validation was missing entirely. Both token forms are now accepted and -validated as 3/4/6/8 hex digits. Corpus case `colors/02-hex.css` + behavior -test; the smoke asserts hex reaches computed styles. The ui bridge test NOTE -about avoiding hex in fixtures is updated — hex stylesheet fixtures are safe -on the current artifact (md5 `858bcb0c…`). - -Diagnostics are no longer silent for parse failures: recovery warnings ride -the compile output (and the IR), and hard errors preserve the warnings -observed before them. - -## Bug 4 (new, perf) — the recovery path is super-linear in the number of invalid rules - -Found while benchmarking adopt/recompile cycles from the ui side (2026-10-12, -same artifact `858bcb0c…`). A stylesheet whose selectors are invalid (e.g. -`.probe-1-0.5123…` — a class token starting with a digit) triggers parser -recovery **per rule**, and compile cost grows super-linearly with the count of -recovered rules. Measured on one component, growing join, 40 rules of -`~45 bytes` each: - -| shape | compile at rule 1 | at rule 20 | at rule 40 | -| --- | --- | --- | --- | -| valid, unique class names | 0.13 ms | ~0.18 ms | ~0.26 ms (linear ✓) | -| invalid idents (recovery per rule) | 0.19 ms | ~12 ms | ~21–37 ms | - -diagnostics are correct in both shapes (1:1 with the invalid rules); linear -memory stays flat (22–24 pages) — so this is the recovery walk itself -(presumably re-scanning accumulated state per recovery), not a leak. Valid -input never enters this path, which is why the corpus/smoke don't see it. - -Repro: compile `n` rules of `.probe-${i}- { color: … }` joined -into one sheet on a single component; compare against the same shape with -valid unique class names. Suggested corpus case: an `n`-rule recovery sheet -with a compile-time budget; suggested fix direction: whatever state the -recovery walk re-traverses per rule (candidate: the shared writer/table state -the packed-tables commits touched) should reset per compile. - -Impact: real-world legacy/generated stylesheets with many invalid rules pay -this silently. The ui never does on valid CSS (its recompiles stay ~0.2 ms). diff --git a/experiments/css/HANDOFF-compute-poison.md b/experiments/css/HANDOFF-compute-poison.md deleted file mode 100644 index c9946c0..0000000 --- a/experiments/css/HANDOFF-compute-poison.md +++ /dev/null @@ -1,86 +0,0 @@ -# Handoff: `compute_tree` poisons later `compile` **and** `prepare` calls - -For an agent picking up the Bombshell CSS engine (`natemoo-re/css`). Written 2026-10-12 from the consuming side (`bombshell-dev/ui`, package `@clack/ui`). Companion doc: `BUGS-from-ui-bridge.md` (same repo) — read it first; this extends it with new findings and the fix brief. - -## The one-paragraph brief - -One `compute_tree` call puts the whole wasm component into a state where every later `compile` **and** `prepare` silently returns null/empty (empty `diagnostics` — the silence is part of the bug). Sheets that already exist keep computing forever, so the per-frame path is fine; what breaks is any stylesheet adoption after the first frame. The UI bridge ships a host-side workaround (fresh component re-instantiation per compile batch) that this fix should make deletable. Fix the engine, prove it in the conformance corpus + smoke, then the UI deletes the workaround. - -## Repos, versions, artifacts - -- Engine: `/Users/nmoo/Developer/natemoo-re/css` — Rust workspace, crates: `css-core` (IR encode/decode, types), `css-compiler` (source → IR, lexbor front-end), `css-runtime` (prepare/cascade/compute), `css-wasm` (the WIT component adapter), `css-conformance` (corpus tests). WIT contract: `wit/css.wit`, interface `bombshell:css/stylesheet@0.3.0`. -- Consumer: `/Users/nmoo/Developer/bombshell-dev/ui`, package `@clack/ui` — vendored the built artifact at `packages/ui/src/css/bombshell-css.wasm`, ABI bindings in `packages/ui/src/css/abi.ts` (adapted from `demo/abi.js`). -- Artifact state: `demo/bombshell_css_wasm.wasm` == `packages/ui/dist/bombshell-css.wasm` == `packages/ui/src/css/bombshell-css.wasm` (md5 `858bcb0c23f8881a8c87254a23418b6a`, 214,412 bytes). Any rebuild should be copied into the ui package (both `src/css/` and `dist/`; the ui build does `cp -f src/css/bombshell-css.wasm dist/`). - -## The full poison matrix (measured 2026-10-12, fresh process each row) - -| sequence | result | -| --- | --- | -| compile → prepare → compute-tree ×N (same sheets) | ✓ stable — 100+ computes, identical output | -| compile → prepare → compute-tree → **compile** | `ir: null`, `diagnostics: []` (known, Bug 3) | -| compile → prepare → compute-tree → **prepare(existing IR bytes)** | **`prepare → null` — NEW** | -| fresh component → `prepare` (the same IR bytes that failed above) | ✓ works — the IR is fine, the component state is not | -| fresh component → `compile` | ✓ works | -| compute-tree with a sheet prepared *after* the poison | returns **0 node results** (empty per-node lists) | - -Key new facts: - -1. **`prepare` joins `compile` in the poisoned set.** It isn't a compiler-side arena; whatever `compute_tree` leaves behind breaks *both* entry points that allocate at call start. -2. **`compute` itself keeps working after the poison** (alloc/free cycles per call succeed — the smoke's "compiles once, computes many" is why CI never saw this). So the global allocator is not broadly corrupted; the failure is specific to what `compile`/`prepare` do first — both go through the compiler/decode front doors that `compute_tree` never re-enters. -3. Failures are **silent**: `diagnostics: []` on every path. Whatever the fix is, a non-recoverable internal state should at minimum emit a diagnostic (or panic — this is a guest module; a loud abort beats silent data loss). - -## Suspect area and leads - -- Adapter: `crates/css-wasm/src/lib.rs` (232 lines — read it whole). - - `prepare` decodes IR and `Box::leak`s it into a `SHEETS` slot (`thread_local!` `RefCell>>`); `release` reconstructs + frees. Decode allocates through the global allocator. - - `compile` → `bombshell_css_compiler::compile(&css, origin, CompileOptions::default())` + `encode_ir`. - - `compute_tree` → `bombshell_css_runtime::cascade::compute_tree`, then the canonical-ABI lift of `list>` — wit-bindgen **frees the lifted lists via `cabi_post_*` hooks**. The bug doc's theory ("allocator/arena state, the compute-tree's lifted-list frees, that compile's next call walks into") is consistent with prepare also dying: both begin by allocating from whatever state the lifts/frees disturbed. -- Recent commits on the exact machinery (suspect range): - - `a8203e6` — "Serialize CSS tokens through one shared writer" - - `a7eba99` — "Pack the color and known-property tables into blobs" - - `eddd906` — "Drop dead Ordering imports from the packed tables" (HEAD) - - Also relevant context: `c0d9fe6` "Make the runtime a per-frame engine: rule index, borrowed cascade, tree API" — the per-frame rework that compute_tree belongs to. -- Things worth instrumenting first: the first allocation each of `compile`/`prepare` performs after a `compute_tree`; the `cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree` free path; whether the blob-packed tables are built in scratch memory that `compute_tree`'s frees disturb; whether `SHEETS` slot bookkeeping (the `slots.push(None)` growth in `prepare`) collides with anything compute_tree wrote. - -## How to reproduce - -`demo/` has the reference host harness (`abi.js`, `smoke.mjs`). Minimal repro (fresh process): - -```js -const component = await loadCssComponent(readFileSync('./demo/bombshell_css_wasm.wasm')); -const ua = component.compile('div { color: red; padding: 1px }', 0); -const sheet = component.prepare(ua.ir); // works -component.computeTree(sheet, nodes, parents, env); // one call is enough -console.log(component.compile('div { color: red }', 2)); // → ir: null, diagnostics: [] -console.log(component.prepare(ua.ir)); // → null (NEW) -``` - -`nodes`/`parents`/`env` shapes are in `demo/abi.js` (`CssNode` 56-byte stride encode; `parents = [NO_PARENT, 0, …]`; env = `{ viewportWidth, viewportHeight, colorScheme: 'dark', reducedMotion, resolution }`). - -Expected after the fix: both calls return live values; the poisoned-handle compute path (sheet handle reused post-release) either works or errors loudly. - -## Definition of done - -1. Engine: a unit/conformance test reproducing the matrix above goes green. Extend `css-conformance` and `demo/smoke.mjs` with the compile→compute→compile/prepare-again sequence so the corpus can't regress. The repo already has uncommitted corpus additions for Bugs 1/2 (`crates/css-conformance/cases/colors/02-hex.css`, `cases/values/02-single-line.css`) — keep them. -2. Loud failure mode: internal-bail paths surface a diagnostic (or guest abort), never a silent empty return. -3. No measurable perf/size regression on the hot path: per-frame `compute_tree` is ~0.02 ms for a 3-node tree today (0.020 ms measured, avg of 1000); keep it within noise. Artifact is size-golfed (nightly `-Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none` + double `wasm-opt -Oz`, see `demo/build.sh`) — report the new byte count; ui copies it verbatim. -4. Consumer-side cleanup (may be a separate step): `bombshell-dev/ui` `packages/ui/src/css/bridge.ts` deletes the workaround in `#recompile` (the `createCssComponentSync` fresh-instantiation per compile batch, currently ~lines 168–193) and prepares new sheets on the long-lived component; `packages/ui/src/css/bridge.test.ts` (16 files / 213 tests, all green at time of writing) plus the headless E2E (`examples/dom-demo`: `node --experimental-strip-types src/headless.ts`) must stay green. - -## Perf baseline (for the comparison after your fix) - -| operation | today | -| --- | --- | -| wasm instantiate (full reload) | 0.151 ms | -| compile, 1.2 KB sheet (UA) | 0.070 ms | -| compile, 740 B sheet | 0.034 ms | -| compile, 60 KB / 500 rules | 4.85 ms | -| prepare, same three sheets | 0.024 / 0.010 / 1.71 ms | -| compute-tree, 3 nodes | 0.020 ms | - -(Precompiling CSS to IR at build time is *not* a substitute for this fix — prepare is poisoned too; the poison fix is the real one. IR portability across instances is confirmed, so a future build-time `.css.ir` artifact path is viable once this lands.) - -## Working agreements - -- The repo has **uncommitted work** (canonical.rs, lib.rs, behavior.rs, demo/abi.js, smoke.mjs + two corpus cases) — it is the in-flight Bugs 1/2 fix; build on it, don't revert it. -- IR/ABI changes require bumping the WIT version (`@0.3.0`) only if the IR format itself changes; a pure state-fix shouldn't. -- Node 22+/24 host, wasm target `wasm32-unknown-unknown`, nightly toolchain required by `demo/build.sh`. diff --git a/experiments/css/README.md b/experiments/css/README.md deleted file mode 100644 index cf398b6..0000000 --- a/experiments/css/README.md +++ /dev/null @@ -1,206 +0,0 @@ -# Bombshell CSS - -The CSS pipeline for [Bombshell UI](https://github.com/bombshell-dev/ui): a -full modern-CSS compiler front-end, a selector/cascade runtime, and a -freestanding wasm component that speaks WIT. - -## Architecture - -``` -CSS text ──▶ bombshell-css-compiler ──▶ typed IR (v2, postcard bytes) - (lexbor C shim: token-level events, - Rust semantics: nesting expansion, - condition parsing, canonicalization) - │ - ▼ - bombshell-css-runtime ──▶ computed declarations - (parcel_selectors matcher, - cascade + inheritance, - custom properties / var(), - media/container/supports eval) - │ - ▼ - bombshell-css-wasm (WIT world `bombshell:css@0.3.0`) -``` - -- **`bombshell-css-core`** — shared contracts: the versioned compiled- - stylesheet IR, the flat DOM snapshot (`TreeSnapshot`), the render - `Environment`, the wire codec, and (behind the default `selectors` - feature) the `parcel_selectors` selector engine that both the compiler - and the runtime drive. The IR half stays `serde` + `postcard` and - `no_std`-capable. -- **`bombshell-css-compiler`** — the front-end. A vendored - [lexbor](https://github.com/lexbor/lexbor) (Apache-2.0) drives a C shim - (`c/shim.c`) that runs lexbor's syntax parser with custom callbacks and - emits a flat event stream of byte spans. Everything semantic happens in - Rust: nesting expansion per the CSS Nesting spec (`&` → parent - substitution), typed condition trees parsed from raw preludes - (`@media`/`@container`/`@supports`), selector validation and specificity - through the shared `parcel_selectors` parser (so the compiler and the - runtime matcher can never disagree about what is valid), and canonical - value serialization (numbers, colors, `light-dark()`, `font-weight` - keywords). Selectors and declaration values cross the boundary as - canonical CSS text; conditions cross as typed trees. -- **`bombshell-css-runtime`** — the engine. Selector matching runs on - `parcel_selectors` (the selector engine lightningcss itself uses) adapted - to the snapshot model. The cascade implements origin/importance, `@layer` - ordering, specificity, and source order; inheritance, custom properties, - `var()` substitution (with fallbacks and cycle detection), and CSS-wide - keywords (`inherit`, `unset`, `initial`, `revert`) resolve per element. -- **`bombshell-css-wasm`** — the WIT adapter. `compile(css, origin)` returns - opaque IR bytes plus diagnostics; `prepare(ir)` decodes and parses a - stylesheet once and returns a handle; `compute(handle, …)` styles one node - and `compute-tree(handle, nodes, parents, env)` styles the whole flat tree - in a single pass — the per-frame entry point, with each node's cascade - running exactly once per frame. `release(handle)` drops a prepared sheet. - The host's linear-memory contract stays free of Rust/JS object layouts, - and the module is freestanding — zero imports (the C side gets a tiny - Rust-backed `malloc` family). -- **`bombshell-css-conformance`** — the corpus: `cases/` fixtures with a - compile/round-trip runner plus behavior tests that compile cases and - assert computed results. - -## Size and speed - -The lexbor front-end replaced lightningcss (MPL-2.0) to cut module weight — -lightningcss was ~94% of the artifact. The size pass on top (vendored-lexbor -trimming, `opt-level = "z"`, nightly `immediate-abort` panics, and the -selector-parsing unification below) roughly halved it again: - -| | before (lightningcss) | after (lexbor) | size + speed pass | -| --- | --- | --- | --- | -| full component (wasm-opt -Oz) | 3.31 MB / 780 KB gzip | 471 KB / 177 KB gzip | **214 KB / 87 KB gzip** | -| compile throughput (77 KB corpus, wasm) | ~15.7 MB/s | ~11.4 MB/s | ~15.1 MB/s | -| whole-tree compute (18-node tree, 1151 rules) | — | ~194 µs/node | **~13 µs/node** | - -`node demo/bench.mjs` re-measures the shipped artifact. - -## The tree ABI - -`compute` receives the whole subtree as a flat, document-ordered node list -plus a parent-index vector (`u32::MAX` = root). This recursion-free shape is -WIT-friendly yet complete: structural pseudo-classes (`:nth-child`, -`:first/last/only-child`), sibling combinators (`+`, `~`), and `:has()` -traversal all work off the snapshot. Ancestors are cascaded automatically so -inheritance, custom properties, and container lookups (`container-type`, -`container-name` from computed styles; sizes from node layout) compose. - -`compute-tree` is the per-frame entry point: one call styles every node, -each node's cascade runs exactly once, and descendants inherit computed -values (var() substitution happens per level, so inheritance carries -computed — not raw — values). The frame loop allocates nothing that -outlives the call; `demo/smoke.mjs` asserts hundreds of full-tree frames -grow memory by zero bytes. Loaded stylesheets borrow the parsed selectors -from the IR — `Stylesheet::from_ir(&ir, …)` is allocation-free apart from -parsing — and a rule index built from the parsed selectors' rightmost -compounds keeps per-node matching proportional to relevant rules, not to -the whole sheet. - -## Build - -```sh -cargo test --workspace - -# The shipped artifact needs nightly (std is rebuilt with immediate-abort -# panics and panic-location strings disabled) plus Binaryen: -./demo/build.sh -``` - -`demo/build.sh` runs `cargo +nightly build -Z build-std=std,panic_abort` with -`-Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none` and then -`wasm-opt -Oz` with the feature flags the module actually uses (bulk-memory, -reference-types, nontrapping-float-to-int, sign-ext, mutable-globals, -multivalue, tail-call — deliberately *not* GC: Binaryen's bottom-type -encoding under `--enable-gc` is not parseable by V8). It then encodes the -optimized module into `wasm.ts` (generated, not hand-maintained) — the same -bundle-friendly scheme the [tty](https://github.com/bombshell-dev/tty) repo -uses: brotli at maximum quality, Z85-encoded into a self-contained -TypeScript module that embeds its own decoder and exports `compiled`, a -ready `WebAssembly.Module`. Hosts import the artifact instead of shipping -and locating a `.wasm` binary, and instantiate it synchronously (the module -is freestanding — zero imports). Verify the encoded artifact by instantiating -it and replaying the demo smoke assertions through it: - -```sh -deno run --allow-read --allow-write tasks/verify-bundle.ts -``` - -A debug build without the size pass: - -```sh -cargo build -p bombshell-css-wasm --target wasm32-unknown-unknown --release -``` - -The vendored C builds with the `cc` crate for both native and wasm32; for -wasm the build is freestanding (stub libc headers under `vendor/stubs`, a -minimal `memcpy` family in the shim, and a Rust-backed `malloc` family in -`alloc_shim.rs`). On macOS an explicit `-isysroot` is probed because -CommandLineTools/Xcode SDK resolution can break. - -## Status - -Implemented and tested: - -- **Parsing/compilation** (lexbor + Rust): full Syntax Level 3/4, nesting - (including `@nest`, nested group rules, `&` inside functional arguments), - statement at-rules (`@layer a, b;`), `@keyframes`/opaque at-rule - preservation, diagnostics with error recovery. -- **Selectors** (parcel_selectors runtime matching): tag/class/ID, - attribute operators with `i`/`s` flags, descendant/child/next-sibling/ - later-sibling combinators, `:is()`, `:where()`, `:not()` (complex args), - `:has()` (traversal), `:nth-child(An+B)`, `:nth-child(An+B of S)`, - `:first/last/only-child`, `:-of-type`, `:root`, `:empty`, `:hover`, - `:active`, `:focus`, `:focus-visible`, `:focus-within`, `:lang()`, - `:dir()`, and attribute-derivable state pseudos (`:disabled`, `:checked`, - `:required`, `:read-only/write`, `:placeholder-shown`, ...). -- **Cascade**: origin + importance ordering, `@layer` (first-appearance - order, nested paths, unlayered wins), specificity (parcel-compatible, - computed Rust-side: `:not`/`:is` take the max of their arguments, `:where` - and `:has` contribute nothing), source order. -- **Values**: canonicalized Rust-side (number minification, named colors to - shortest-hex, `light-dark()` resolved to its light branch, `font-weight` - keywords; modern color functions preserved: `oklab`/`oklch`/`lab`/`lch`/ - `color()`/`color-mix()`/relative `rgb()`), custom properties, `var()` with - fallbacks and cycle detection, CSS-wide keywords. -- **Conditions**: media (range/interval syntax, discrete features, - `not`/`and`/`or`, OR lists), container queries (named + anonymous, - `width`/`height`/`inline-size`/`block-size`/`aspect-ratio`/`orientation`, - container units `cqw`/`cqh`/... in query values), `@supports` with - compile-time leaf evaluation. - -Deliberately not implemented yet (the IR preserves the syntax; the runtime -skips it with the boundary unchanged): - -- `:nth-col()`, shadow-DOM selectors (`:host`, `::slotted`, `::part`) — - parsed but never matched. -- Pseudo-elements (`::before`, `::after`, `::selection`, ...) — parsed but - never matched; generated content is a terminal-layout question. -- Style queries (`style(color: red)`) and scroll-state queries — parsed, - preserved, not evaluated. -- Animations/transitions (`@keyframes` is preserved verbatim), `@property` - registration semantics, initial-value tables for unset properties, - `@scope` proximity, and container `em` resolution against the container's - own font-size (currently a 16px base). -- Value validation is partial: structural failures and color-property - values are checked (colors are single keywords/hashes/functions); other - property grammars are not validated (lightningcss validated everything — - a curated per-property grammar table is the eventual replacement). - -## Provenance - -- [lexbor](https://github.com/lexbor/lexbor) (Apache-2.0) is vendored under - `crates/css-compiler/vendor/lexbor` and trimmed to the syntax parser: - only `core/*` memory/string/number primitives, `css/{css,log,parser,rule}.c`, - and `css/syntax/*` are compiled (the selectors, typed at-rule/property, - and value layers are dropped — Rust owns those semantics). Local patches - to that subset: lexbor's log formatting is gutted (message text is never - read; `parser->status` is the only observable) and the token-error path - no longer serializes token text. -- `parcel_selectors` and `cssparser` are the same versions lightningcss - 1.0.0-alpha.72 depends on; selector validation and specificity come from - the same `parcel_selectors` parser the runtime matcher uses. -- Conformance cases are written against the CSSWG specifications (CSS - Nesting 1, Selectors 4, Media Queries 4, Containment 3, Cascade 5, Color - 4/5); lightningcss upstream fixtures informed several nesting and - condition shapes in the original spike. This repository's own code is - MIT. diff --git a/experiments/css/demo/README.md b/experiments/css/demo/README.md deleted file mode 100644 index 7cd9949..0000000 --- a/experiments/css/demo/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# Demo - -A minimal browser demo that drives the full pipeline through the raw WIT -canonical ABI of `bombshell-css-wasm`: - -1. compile the editable stylesheet (lexbor front-end → typed IR), -2. decode the IR with a small postcard reader, -3. resolve computed styles for a small tree snapshot - (container queries, `@media`, nesting, custom properties), -4. display the compiled rules and the computed declarations. - -`abi.js` is the interesting part: it implements the component-model -canonical ABI by hand (string/list encoding, the `compiled-sheet` record, -the 56-byte `node` record) plus a postcard reader for IR v2. It follows the -wit-bindgen allocation protocol: host inputs are allocated with -`cabi_realloc` and transfer ownership to the module (never freed -host-side), and the callee-owned return areas are released with the -`cabi_post_*` hooks after reading. The editor drives `compile` per -keystroke, `prepare` once, and `compute-tree` per render — one canonical-ABI -crossing per frame for the whole tree. - -Build and run: - -```sh -./demo/build.sh # nightly + build-std + wasm-opt size pass - -node demo/smoke.mjs # ABI smoke test without a browser (checks the - # frame loop leaks nothing) -node demo/bench.mjs # compile-throughput benchmark -python3 -m http.server 8080 # then open http://localhost:8080/demo/ -``` - -The optimized artifact is git-ignored; build it locally as shown above. The -build also encodes it into `wasm.ts` at the repo root — the bundle-friendly -artifact hosts import instead of the binary. diff --git a/experiments/css/demo/abi.js b/experiments/css/demo/abi.js deleted file mode 100644 index cc5873d..0000000 --- a/experiments/css/demo/abi.js +++ /dev/null @@ -1,478 +0,0 @@ -// Canonical ABI bindings for the bombshell:css component (wasm32). -// -// All in-memory layouts follow the component-model canonical ABI. Strings -// and lists are (ptr, len) pairs pointing at wasm memory; string bytes are -// copied into linear memory via `cabi_realloc`. - -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); - -export const NO_PARENT = 0xffffffff; - -export async function loadCssComponent(source) { - const bytes = source instanceof Uint8Array ? source : new Uint8Array(await source.arrayBuffer()); - const instance = (await WebAssembly.instantiate(bytes, {})).instance; - return createComponent(instance); -} - -export function loadCssComponentFromModule(module) { - return createComponent(new WebAssembly.Instance(module, {})); -} - -function createComponent(instance) { - const exports = instance.exports; - let memory = new Uint8Array(exports.memory.buffer); - let view = new DataView(exports.memory.buffer); - const realloc = exports.cabi_realloc; - const compileFn = exports['bombshell:css/stylesheet@0.3.0#compile']; - const computeFn = exports['bombshell:css/stylesheet@0.3.0#compute']; - const prepareFn = exports['bombshell:css/stylesheet@0.3.0#prepare']; - const releaseFn = exports['bombshell:css/stylesheet@0.3.0#release']; - const computeTreeFn = exports['bombshell:css/stylesheet@0.3.0#compute-tree']; - const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree']; - const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compile']; - const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute']; - function refresh() { - memory = new Uint8Array(exports.memory.buffer); - view = new DataView(exports.memory.buffer); - } - - // Host allocations use `cabi_realloc`: wit-bindgen lifts string/list - // arguments zero-copy (it takes ownership of the buffer and frees it with - // its own allocator), so inputs must come from the allocator the callee - // will free with, and must NOT be freed host-side. Callee-owned return - // areas are released by the `cabi_post_*` hooks after the host reads. - function allocate(bytes) { - const pointer = realloc(0, 0, 1, bytes.byteLength); - refresh(); - memory.set(bytes, pointer); - return pointer; - } - - function writeString(text) { - return allocate(encoder.encode(text)); - } - - function readString(pointer, length) { - return decoder.decode(memory.subarray(pointer, pointer + length)); - } - - function u32(pointer) { - return view.getUint32(pointer, true); - } - - function f32(pointer) { - return view.getFloat32(pointer, true); - } - - function stringPointer(text) { - const bytes = encoder.encode(text); - return { pointer: allocate(bytes), length: bytes.byteLength }; - } - - function stringListPointer(items) { - if (items.length === 0) { - return { pointer: 0, count: 0 }; - } - const entries = items.map(stringPointer); - const pointer = realloc(0, 0, 4, entries.length * 8); - refresh(); - entries.forEach((entry, i) => { - view.setUint32(pointer + i * 8, entry.pointer, true); - view.setUint32(pointer + i * 8 + 4, entry.length, true); - }); - return { pointer, count: entries.length }; - } - - function attributeListPointer(attributes) { - if (attributes.length === 0) { - return { pointer: 0, count: 0 }; - } - const entries = attributes.map((attribute) => ({ - name: stringPointer(attribute.name), - value: stringPointer(attribute.value), - })); - const pointer = realloc(0, 0, 4, entries.length * 16); - refresh(); - entries.forEach((entry, i) => { - const base = pointer + i * 16; - view.setUint32(base, entry.name.pointer, true); - view.setUint32(base + 4, entry.name.length, true); - view.setUint32(base + 8, entry.value.pointer, true); - view.setUint32(base + 12, entry.value.length, true); - }); - return { pointer, count: entries.length }; - } - - function compile(css, origin = 2) { - const bytes = encoder.encode(css); - const cssPointer = allocate(bytes); - const result = compileFn(cssPointer, bytes.byteLength, origin); - refresh(); - // compiled-sheet: option> (disc u8@0, ptr@4, len@8) - // + list (ptr@12, len@16) - // The option discriminant is one byte; bytes 1..3 are padding the - // canonical ABI leaves undefined, so they must not leak into the check. - const hasIr = view.getUint8(result) === 1; - const irPointer = u32(result + 4); - const irLength = u32(result + 8); - const diagnosticsPointer = u32(result + 12); - const diagnosticsCount = u32(result + 16); - const ir = hasIr ? memory.slice(irPointer, irPointer + irLength) : null; - const diagnostics = []; - for (let i = 0; i < diagnosticsCount; i++) { - // diagnostic: severity u8@0, code (ptr@4, len@8), message (ptr@12, len@16) - const base = diagnosticsPointer + i * 16; - diagnostics.push({ - severity: view.getUint8(base), - code: readString(u32(base + 4), u32(base + 8)), - message: readString(u32(base + 12), u32(base + 16)), - }); - } - // The callee-allocated return area (and the strings it points to) is - // released by the post-return hook, after the host has finished reading. - postCompile(result); - return { ir, diagnostics }; - } - - /// Decode and parse IR once; returns a handle for `compute`. - function prepare(ir) { - const irBytes = ir instanceof Uint8Array ? ir : new Uint8Array(ir); - const irPointer = allocate(irBytes); - const result = prepareFn(irPointer, irBytes.byteLength); - refresh(); - // option: disc u8@0, value@4 — discriminant is one byte; the - // canonical ABI leaves bytes 1..3 undefined, so read only the byte. - return view.getUint8(result) === 1 ? u32(result + 4) : null; - } - - /// Resolve computed styles for every node in one pass. Returns one - /// declaration list per node, index-aligned with `nodes`. - function computeTree(sheet, nodes, parents, env) { - const stride = 56; - const data = new Uint8Array(nodes.length * stride); - const dataView = new DataView(data.buffer); - nodes.forEach((node, i) => { - const base = i * stride; - const tag = stringPointer(node.tag); - dataView.setUint32(base, tag.pointer, true); - dataView.setUint32(base + 4, tag.length, true); - const id = node.id ? stringPointer(node.id) : null; - dataView.setUint32(base + 8, id ? 1 : 0, true); - if (id) { - dataView.setUint32(base + 12, id.pointer, true); - dataView.setUint32(base + 16, id.length, true); - } - const classes = stringListPointer(node.classes ?? []); - dataView.setUint32(base + 20, classes.pointer, true); - dataView.setUint32(base + 24, classes.count, true); - const attributes = attributeListPointer(node.attributes ?? []); - dataView.setUint32(base + 28, attributes.pointer, true); - dataView.setUint32(base + 32, attributes.count, true); - dataView.setUint8(base + 36, node.state ?? 0); - const hasWidth = node.width !== undefined && node.width !== null; - const hasHeight = node.height !== undefined && node.height !== null; - dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); - dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); - dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); - dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); - }); - const nodesPointer = allocate(data); - - const parentBytes = new Uint8Array(parents.length * 4); - const parentView = new DataView(parentBytes.buffer); - parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); - const parentPointer = allocate(parentBytes); - - const result = computeTreeFn( - sheet, - nodesPointer, - nodes.length, - parentPointer, - parents.length, - env?.viewportWidth != null ? 1 : 0, - env?.viewportWidth ?? 0, - env?.viewportHeight != null ? 1 : 0, - env?.viewportHeight ?? 0, - env?.colorScheme === 'dark' ? 1 : 0, - env?.reducedMotion ? 1 : 0, - env?.resolution ?? 1, - ); - refresh(); - // list>: (ptr@0, len@4); each entry is - // (ptr@0, len@4) over 16-byte name/value pairs. - const outer = u32(result); - const outerCount = u32(result + 4); - const perNode = []; - for (let i = 0; i < outerCount; i++) { - const entryBase = outer + i * 8; - const pointer = u32(entryBase); - const count = u32(entryBase + 4); - const declarations = []; - for (let j = 0; j < count; j++) { - const base = pointer + j * 16; - declarations.push([ - readString(u32(base), u32(base + 4)), - readString(u32(base + 8), u32(base + 12)), - ]); - } - perNode.push(declarations); - } - postComputeTree(result); - return perNode; - } - - function release(sheet) { - releaseFn(sheet); - } - - function compute(sheet, nodes, parents, target, env) { - // node: tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), - // classes list (ptr@20, len@24), attributes list - // (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), - // height option (disc@48, f32@52) — 56 bytes per node. - const stride = 56; - const data = new Uint8Array(nodes.length * stride); - const dataView = new DataView(data.buffer); - nodes.forEach((node, i) => { - const base = i * stride; - const tag = stringPointer(node.tag); - dataView.setUint32(base, tag.pointer, true); - dataView.setUint32(base + 4, tag.length, true); - const id = node.id ? stringPointer(node.id) : null; - dataView.setUint32(base + 8, id ? 1 : 0, true); - if (id) { - dataView.setUint32(base + 12, id.pointer, true); - dataView.setUint32(base + 16, id.length, true); - } - const classes = stringListPointer(node.classes ?? []); - dataView.setUint32(base + 20, classes.pointer, true); - dataView.setUint32(base + 24, classes.count, true); - const attributes = attributeListPointer(node.attributes ?? []); - dataView.setUint32(base + 28, attributes.pointer, true); - dataView.setUint32(base + 32, attributes.count, true); - dataView.setUint8(base + 36, node.state ?? 0); - const hasWidth = node.width !== undefined && node.width !== null; - const hasHeight = node.height !== undefined && node.height !== null; - dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); - dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); - dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); - dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); - }); - const nodesPointer = allocate(data); - - const parentBytes = new Uint8Array(parents.length * 4); - const parentView = new DataView(parentBytes.buffer); - parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); - const parentPointer = allocate(parentBytes); - - const result = computeFn( - sheet, - nodesPointer, - nodes.length, - parentPointer, - parents.length, - target, - env?.viewportWidth != null ? 1 : 0, - env?.viewportWidth ?? 0, - env?.viewportHeight != null ? 1 : 0, - env?.viewportHeight ?? 0, - env?.colorScheme === 'dark' ? 1 : 0, - env?.reducedMotion ? 1 : 0, - env?.resolution ?? 1, - ); - refresh(); - // list: (ptr@0, len@4) - const pointer = u32(result); - const count = u32(result + 4); - const declarations = []; - for (let i = 0; i < count; i++) { - // computed-property: name (ptr@0, len@4), value (ptr@8, len@12) - const base = pointer + i * 16; - declarations.push([ - readString(u32(base), u32(base + 4)), - readString(u32(base + 8), u32(base + 12)), - ]); - } - postCompute(result); - return declarations; - } - - return { compile, prepare, compute, computeTree, release, raw: exports }; -} - -// --------------------------------------------------------------------------- -// Postcard reader for CompiledStylesheet (IR_VERSION 2) -// --------------------------------------------------------------------------- - -export function decodeIr(bytes) { - const reader = new IrReader(bytes); - return { - version: reader.varint(), - origin: reader.byte(), - rules: reader.vec(() => reader.rule()), - layerOrder: reader.vec(() => reader.stringVec()), - keyframes: reader.vec(() => ({ name: reader.string(), body: reader.string() })), - opaque: reader.vec(() => ({ text: reader.string() })), - warnings: reader.vec(() => reader.diagnostic()), - }; -} - -class IrReader { - constructor(bytes) { - this.bytes = bytes; - this.cursor = 0; - this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); - } - - byte() { - return this.bytes[this.cursor++]; - } - - varint() { - let value = 0; - let shift = 0; - for (;;) { - const byte = this.bytes[this.cursor++]; - value |= (byte & 0x7f) << shift; - if ((byte & 0x80) === 0) { - break; - } - shift += 7; - } - return value >>> 0; - } - - zigzag() { - const n = this.varint(); - return (n >>> 1) ^ -(n & 1); - } - - string() { - const length = this.varint(); - const text = decoder.decode(this.bytes.subarray(this.cursor, this.cursor + length)); - this.cursor += length; - return text; - } - - stringVec() { - return this.vec(() => this.string()); - } - - vec(read) { - const count = this.varint(); - const items = []; - for (let i = 0; i < count; i++) { - items.push(read()); - } - return items; - } - - diagnostic() { - return { severity: this.byte(), code: this.string(), message: this.string() }; - } - - rule() { - return { - selector: this.string(), - specificity: this.varint(), - layer: this.option(() => this.stringVec()), - media: this.vec(() => this.mediaCondition()), - containers: this.vec(() => ({ - name: this.option(() => this.string()), - condition: this.containerCondition(), - })), - supports: this.vec(() => this.supportsCondition()), - declarations: this.vec(() => ({ - name: this.string(), - value: this.string(), - important: this.byte() === 1, - })), - order: this.varint(), - }; - } - - option(read) { - if (this.byte() === 0) { - return null; - } - return read(); - } - - mediaCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'feature', feature: this.feature() }; - case 1: return { type: 'not', inner: this.mediaCondition() }; - case 2: return { type: 'and', conditions: this.vec(() => this.mediaCondition()) }; - case 3: return { type: 'or', conditions: this.vec(() => this.mediaCondition()) }; - default: return { type: 'unknown' }; - } - } - - feature() { - const name = this.string(); - const kind = this.varint(); - switch (kind) { - case 0: return { name, kind: { type: 'plain', value: this.featureValue() } }; - case 1: return { name, kind: { type: 'boolean' } }; - case 2: return { name, kind: { type: 'range', cmp: this.varint(), value: this.featureValue() } }; - case 3: return { - name, - kind: { - type: 'interval', - start: this.featureValue(), - startCmp: this.varint(), - end: this.featureValue(), - endCmp: this.varint(), - }, - }; - default: throw new Error(`unknown feature kind ${kind}`); - } - } - - featureValue() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'px', value: this.f32() }; - case 1: return { type: 'length', value: this.f32(), unit: this.string() }; - case 2: return { type: 'number', value: this.f32() }; - case 3: return { type: 'integer', value: this.zigzag() }; - case 4: return { type: 'boolean', value: this.byte() === 1 }; - case 5: return { type: 'dppx', value: this.f32() }; - case 6: return { type: 'ratio', a: this.f32(), b: this.f32() }; - case 7: return { type: 'ident', value: this.string() }; - default: return { type: 'unknown' }; - } - } - - f32() { - const value = this.view.getFloat32(this.cursor, true); - this.cursor += 4; - return value; - } - - containerCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'size', feature: this.feature() }; - case 1: return { type: 'style', text: this.string() }; - case 2: return { type: 'not', inner: this.containerCondition() }; - case 3: return { type: 'and', conditions: this.vec(() => this.containerCondition()) }; - case 4: return { type: 'or', conditions: this.vec(() => this.containerCondition()) }; - default: return { type: 'unknown' }; - } - } - - supportsCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'declaration', name: this.string(), value: this.string(), supported: this.byte() === 1 }; - case 1: return { type: 'selector', text: this.string(), supported: this.byte() === 1 }; - case 2: return { type: 'not', inner: this.supportsCondition() }; - case 3: return { type: 'and', conditions: this.vec(() => this.supportsCondition()) }; - case 4: return { type: 'or', conditions: this.vec(() => this.supportsCondition()) }; - default: return { type: 'unknown' }; - } - } -} diff --git a/experiments/css/demo/bench.mjs b/experiments/css/demo/bench.mjs deleted file mode 100644 index dbb3c72..0000000 --- a/experiments/css/demo/bench.mjs +++ /dev/null @@ -1,145 +0,0 @@ -// Benchmarks against the shipped component: compile throughput and the -// per-frame `compute` path real hosts drive. The corpus is generated -// deterministically, so numbers are comparable across machines and runs. -import { readFileSync } from "node:fs"; -import { loadCssComponent, decodeIr, NO_PARENT } from "./abi.js"; - -const component = await loadCssComponent( - readFileSync(new URL("./bombshell_css_wasm.wasm", import.meta.url)), -); - -// A synthetic-but-realistic stylesheet: 1140 top-level class rules cycling -// real property/value shapes, plus nesting, media, container, keyframes, -// and supports blocks in the mix. -const PROPS = [ - ["color", ["red", "#34a", "oklch(70% .2 40)", "rgb(10 20 30 / 50%)"]], - ["background-color", ["transparent", "light-dark(white, black)"]], - ["margin", ["0", "4px", "1.5rem", "8px 16px"]], - ["padding", ["0 auto", "2px 4px 6px"]], - ["font-size", ["12px", "1.25rem", "100%"]], - ["border-color", ["red", "color-mix(in oklab, red 40%, blue)"]], - ["width", ["auto", "100px", "50%", "min(10px, 2em)"]], - ["display", ["block", "flex", "none"]], - ["opacity", ["1", ".5"]], - ["--custom", ["var(--x, 1px)", "oklch(50% .1 200)"]], -]; -const VARIANTS = [ - (n) => `.c${n}`, - (n) => `.c${n}:hover`, - (n) => `.c${n}[data-x='${n}']`, - (n) => `.c${n} > .child`, - (n) => `:is(.c${n}, .c${n + 1})`, - (n) => `.c${n}:nth-child(2n + 1)`, -]; -let css = ""; -let n = 0; -while (n < 1140) { - const [prop, values] = PROPS[n % PROPS.length]; - const value = values[(n / PROPS.length) % values.length | 0]; - css += `${VARIANTS[n % VARIANTS.length](n)} { ${prop}: ${value} }\n`; - n++; -} -css += `@media (min-width: 24em) { ${VARIANTS[0](n)} { ${PROPS[0][0]}: blue } ${VARIANTS[1](n + 1)} { ${PROPS[2][0]}: 2px } }\n`; -n += 2; -css += `@container panel (inline-size > 20em) { .c${n} { ${PROPS[5][0]}: green } }\n`; -n += 1; -css += `.panel { container-type: inline-size; container-name: panel }\n`; -css += `.card { padding: 4px; & > .title { font-weight: bold } .body { margin: 0 } }\n`; -css += `@layer base { .c${n} { ${PROPS[0][0]}: red } .c${n + 1} { ${PROPS[3][0]}: 1px } }\n`; -n += 2; -css += `@supports (color: oklch(50% .1 200)) { .c${n} { ${PROPS[0][0]}: oklch(50% .1 200) } }\n`; -n += 1; -css += `@keyframes pulse { 0% { opacity: 1 } 100% { opacity: 0 } }\n`; -css += `.c${n} { ${PROPS[8][0]}: var(--x, .4) }\n`; - -// --------------------------------------------------------------------------- -// Compile throughput -// --------------------------------------------------------------------------- - -const run = () => component.compile(css, "author"); - -for (let i = 0; i < 5; i++) run(); - -const N = 50; -const times = []; -let rules = 0; -for (let i = 0; i < N; i++) { - const start = performance.now(); - const result = run(); - times.push(performance.now() - start); - if (i === 0) { - const ir = decodeIr(result.ir); - rules = ir.rules.length; - } -} -times.sort((a, b) => a - b); -const median = times[N >> 1]; -console.log( - `compile (wasm): median ${median.toFixed(2)} ms → ${(css.length / 1024 / median * 1000).toFixed(1)} KB/s (${rules} rules)`, -); - -// --------------------------------------------------------------------------- -// Per-frame compute: a UI-shaped tree over the corpus's own class names -// --------------------------------------------------------------------------- - -const compiled = run(); -const ir = compiled.ir; -const sheet = decodeIr(ir); -const handle = component.prepare(ir); -if (handle === null) throw new Error("prepare rejected the IR"); - -// Class vocabulary from the compiled selectors, most frequent first. -const freq = new Map(); -for (const rule of sheet.rules) { - for (const match of rule.selector.matchAll(/\.([A-Za-z][\w-]*)/g)) { - freq.set(match[1], (freq.get(match[1]) ?? 0) + 1); - } -} -const classes = [...freq.entries()].sort((a, b) => b[1] - a[1]).map(([c]) => c); -if (classes.length === 0) classes.push("node"); - -// A plausible UI tree: a container panel with a grid of cards, each with a -// title and a couple of children — 1 + 4 + 4*(1+3) = 21 nodes, sizes set on -// the container so size queries can match. -const tree = [{ tag: "root", classes: [classes[0]], width: 1024, height: 768, state: 0 }]; -const parents = [NO_PARENT]; -const panel = tree.length; -tree.push({ tag: "panel", classes: [classes[1] ?? classes[0]], width: 1024, height: 768, state: 0 }); -parents.push(0); -for (let i = 0; i < 4; i++) { - const card = tree.length; - tree.push({ tag: "card", classes: [classes[i % classes.length]], state: i === 1 ? 1 : 0 }); - parents.push(panel); - for (let j = 0; j < 3; j++) { - tree.push({ tag: "text", classes: [classes[(i + j + 2) % classes.length]], state: 0 }); - parents.push(card); - } -} - -const env = { viewportWidth: 1024, viewportHeight: 768, colorScheme: "dark", reducedMotion: false, resolution: 1 }; - -// Warm. -for (const target of tree.keys()) component.compute(handle, tree, parents, target, env); - -const FRAMES = 100; -const frameTimes = []; -for (let f = 0; f < FRAMES; f++) { - const start = performance.now(); - component.computeTree(handle, tree, parents, env); - frameTimes.push(performance.now() - start); -} -frameTimes.sort((a, b) => a - b); -const frame = frameTimes[FRAMES >> 1]; -console.log( - `compute-tree (wasm): median ${frame.toFixed(3)} ms/frame → ${(frame * 1000 / tree.length).toFixed(1)} µs/node × ${tree.length} nodes`, -); - -// Memory stability: a frame loop must not grow linear memory. -const before = component.raw.memory.buffer.byteLength; -for (let f = 0; f < 2000; f++) { - component.computeTree(handle, tree, parents, env); -} -const growth = component.raw.memory.buffer.byteLength - before; -console.log(`compute loop: 2000 frames, memory grew ${growth} bytes`); -if (growth !== 0) throw new Error(`per-frame leak: ${growth} bytes`); -component.release(handle); diff --git a/experiments/css/demo/build.sh b/experiments/css/demo/build.sh deleted file mode 100755 index b78d474..0000000 --- a/experiments/css/demo/build.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh -# Build the shipped wasm component. Size-minimal: the nightly toolchain is -# required for the std rebuild (panic_immediate_abort strips panic payloads) -# and for the rustc location-detail/fmt-debug flags that keep panic location -# strings and Debug impls out of linear memory. -set -e - -FLAGS="-Zunstable-options -Cpanic=immediate-abort -Zlocation-detail=none -Zfmt-debug=none" - -RUSTFLAGS="$FLAGS" cargo +nightly build -p bombshell-css-wasm --target wasm32-unknown-unknown --release \ - -Z build-std=std,panic_abort - -# Feature set must match what the module uses: no GC (binaryen's bottom-type -# encoding with GC enabled is not parseable by V8). A second -Oz pass shaves -# the last few hundred bytes. -wasm-opt target/wasm32-unknown-unknown/release/bombshell_css_wasm.wasm \ - -Oz \ - --enable-bulk-memory --enable-reference-types --enable-nontrapping-float-to-int \ - --enable-sign-ext --enable-mutable-globals --enable-multivalue --enable-tail-call \ - --strip-debug --strip-producers \ - -o demo/bombshell_css_wasm.wasm - -wasm-opt demo/bombshell_css_wasm.wasm \ - -Oz \ - --enable-bulk-memory --enable-reference-types --enable-nontrapping-float-to-int \ - --enable-sign-ext --enable-mutable-globals --enable-multivalue --enable-tail-call \ - -o demo/bombshell_css_wasm.wasm - -stat -f "%z bytes" demo/bombshell_css_wasm.wasm - -# Encode the optimized module into the bundle-friendly artifact: a -# self-contained wasm.ts exporting the compiled module (same scheme as the -# tty repo). Needs deno. -deno run --allow-read --allow-write tasks/bundle-wasm.ts diff --git a/experiments/css/demo/index.html b/experiments/css/demo/index.html deleted file mode 100644 index f4092a0..0000000 --- a/experiments/css/demo/index.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Bombshell CSS - - - -
-

Bombshell CSS

-

- lightningcss front-end → typed IR → parcel_selectors runtime, running - as a freestanding wasm module. -

-
-
-
- - - - - … -
-
- -

-      
-
- - - diff --git a/experiments/css/demo/main.js b/experiments/css/demo/main.js deleted file mode 100644 index 47d0ef1..0000000 --- a/experiments/css/demo/main.js +++ /dev/null @@ -1,89 +0,0 @@ -import { loadCssComponent, decodeIr, NO_PARENT } from './abi.js'; - -const source = document.querySelector('#source'); -const output = document.querySelector('#output'); -const status = document.querySelector('#status'); -const widthInput = document.querySelector('#viewport-width'); - -const component = await loadCssComponent(await (await fetch('./bombshell_css_wasm.wasm')).blob()); -let sheet_handle = null; - -// The demo tree: a panel container with two cards, one focused. -const nodes = [ - { tag: 'root', classes: ['root', 'panel'], width: 400, height: null, state: 0 }, - { tag: 'card', classes: ['card'], state: 0 }, - { tag: 'card', classes: ['card', 'special'], state: 1 }, // focused - { tag: 'title', classes: ['title'] }, - { tag: 'title', classes: ['title'] }, -]; -const parents = [NO_PARENT, 0, 0, 1, 2]; -const tags = ['root', 'card', 'card.special (focus)', 'title', 'title']; - -function format(condition) { - if (!condition || condition.type === 'unknown') return 'unknown'; - switch (condition.type) { - case 'feature': return `${condition.feature.name}`; - case 'not': return `not ${format(condition.inner)}`; - case 'and': return condition.conditions.map(format).join(' and '); - case 'or': return condition.conditions.map(format).join(' or '); - case 'size': return `${condition.feature.name}`; - case 'style': return condition.text; - case 'declaration': return `${condition.name}: ${condition.value}`; - case 'selector': return condition.text; - default: return condition.type; - } -} - -function run() { - try { - const { ir, diagnostics } = component.compile(source.value, 2); - const lines = []; - for (const diagnostic of diagnostics) { - lines.push(`${diagnostic.severity === 0 ? 'error' : 'warning'} [${diagnostic.code}] ${diagnostic.message}`); - } - - if (ir) { - const sheet = decodeIr(ir); - if (sheet_handle !== null) component.release(sheet_handle); - sheet_handle = component.prepare(ir); - if (sheet_handle === null) throw new Error('prepare rejected the IR'); - lines.push(`IR v${sheet.version} — ${ir.byteLength} bytes, ${sheet.rules.length} rules`); - lines.push(''); - const env = { - viewportWidth: Number(widthInput.value) || null, - viewportHeight: 300, - colorScheme: 'dark', - reducedMotion: false, - resolution: 1, - }; - const computedPerNode = component.computeTree(sheet_handle, nodes, parents, env); - nodes.forEach((node, index) => { - lines.push(`${tags[index]}:`); - for (const [name, value] of computedPerNode[index]) { - lines.push(` ${name}: ${value}`); - } - lines.push(''); - }); - lines.push('compiled rules:'); - for (const rule of sheet.rules) { - const conditions = [ - ...rule.media.map((m) => `@media ${format(m)}`), - ...rule.containers.map((c) => `@container ${c.name ? c.name + ' ' : ''}${format(c.condition)}`), - ...rule.supports.map((s) => `@supports ${format(s)}`), - ]; - const prefix = conditions.length ? `${conditions.join(' AND ')} » ` : ''; - const layer = rule.layer ? `@layer ${rule.layer.join('.')} » ` : ''; - lines.push(` ${prefix}${layer}${rule.selector}`); - } - } - output.textContent = lines.join('\n'); - status.value = ir ? 'ok' : 'compile failed'; - } catch (error) { - status.value = 'error'; - output.textContent = error instanceof Error ? error.stack : String(error); - } -} - -source.addEventListener('input', run); -widthInput.addEventListener('input', run); -run(); diff --git a/experiments/css/demo/smoke.mjs b/experiments/css/demo/smoke.mjs deleted file mode 100644 index 1ac6ce9..0000000 --- a/experiments/css/demo/smoke.mjs +++ /dev/null @@ -1,140 +0,0 @@ -// Smoke test: drive the raw canonical ABI from Node. -// Usage: node demo/smoke.mjs [path-to-wasm] - -import { readFileSync } from 'node:fs'; -import { loadCssComponent, decodeIr, NO_PARENT } from './abi.js'; - -const path = process.argv[2] ?? new URL('./bombshell_css_wasm.wasm', import.meta.url).pathname; -const component = await loadCssComponent(readFileSync(path)); - -const css = ` - :root { --brand: oklch(70% .2 40); color: var(--brand) } - .panel { container-type: inline-size; container-name: panel } - .card { padding: 4px; color: light-dark(white, black) } - .card { & > .title { font-weight: bold } } - @media (min-width: 24em) { - @container panel (inline-size > 20em) { - .card { border-color: var(--brand) } - } - } -`; - -const { ir, diagnostics } = component.compile(css, 2); -console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); -if (!ir) { - throw new Error('compile returned no IR'); -} -console.log('ir bytes:', ir.byteLength); -const sheet_handle = component.prepare(ir); -if (sheet_handle === null) { - throw new Error('prepare rejected the IR'); -} - -const sheet = decodeIr(ir); -console.log('ir version:', sheet.version); -for (const rule of sheet.rules) { - console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); -} - -const tree = [ - { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, // container - { tag: 'card', classes: ['card'] }, - { tag: 'title', classes: ['title'] }, -]; -const parents = [NO_PARENT, 0, 1]; - -// Node 0 as a named container via container-type comes from computed styles; -// this fixture relies on the width. Compute each node with a dark viewport. -const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; -for (const target of [0, 1, 2]) { - const computed = component.compute(sheet_handle, tree, parents, target, env); - console.log(`node ${target}:`, JSON.stringify(computed)); -} - -// Frame loop: `compute` runs per animation frame in real hosts, so its -// memory must not grow over time. The component caches the parsed sheet for -// identical IR bytes; a growing trace means the cache is leaking. -function memoryPages() { - return component.raw.memory.buffer.byteLength; -} -// The tree API must agree with the per-node API. -const perNode = []; -for (const target of tree.keys()) { - perNode.push(component.compute(sheet_handle, tree, parents, target, env)); -} -const whole = component.computeTree(sheet_handle, tree, parents, env); -if (JSON.stringify(perNode) !== JSON.stringify(whole)) { - throw new Error('computeTree disagrees with compute'); -} - -// Re-prepare + release exercises the slot lifecycle before the loop. -const other = component.compile('.x { color: blue }', 2); -const other_handle = component.prepare(other.ir); -component.compute(other_handle, tree, parents, 2, env); -component.release(other_handle); -for (let i = 0; i < 500; i++) { - component.compute(sheet_handle, tree, parents, 2, env); -} -if (memoryPages() !== memoryPages()) throw new Error('unstable'); -const pagesAfterFrames = memoryPages(); -for (let i = 0; i < 500; i++) { - for (const target of tree.keys()) { - component.compute(sheet_handle, tree, parents, target, env); - } - component.computeTree(sheet_handle, tree, parents, env); -} -const growth = memoryPages() - pagesAfterFrames; -console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); -if (growth > 0) { - throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); -} - -// Option-returning calls after compute-tree: the shared return area holds a -// pointer from compute-tree's lowering, so the option discriminant's padding -// bytes are no longer zero. Reading the disc as anything wider than one byte -// misreads these results as absent. -const hot = component.compile('.card { color: #fff }', 2); -if (hot.ir === null) { - throw new Error('compile after compute-tree reported no IR'); -} -const hotHandle = component.prepare(hot.ir); -if (hotHandle === null) { - throw new Error('prepare after compute-tree rejected valid IR'); -} -component.release(hotHandle); - -// The rest of the poison matrix: re-preparing the ORIGINAL IR bytes… -const rePrepare = component.prepare(ir); -if (rePrepare === null) { - throw new Error('re-prepare of the existing IR rejected valid bytes'); -} -// …and computing on a sheet adopted after compute-tree. -const adopted = component.compile('.card { color: #0f0 }', 2); -if (adopted.ir === null) { - throw new Error('compile after compute-tree reported no IR'); -} -const adoptedHandle = component.prepare(adopted.ir); -if (adoptedHandle === null) { - throw new Error('prepare after compute-tree rejected valid IR'); -} -const afterPoison = component.computeTree(adoptedHandle, tree, parents, env); -if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { - throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); -} -component.release(rePrepare); -component.release(adoptedHandle); - -// Diagnostics surface through the ABI: an invalid color warns and the rest -// of the rule recovers. -const warned = component.compile('.card { color: notacolor; padding: 4px }', 2); -if ( - warned.ir === null || - !warned.diagnostics.some( - (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), - ) -) { - throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); -} - -component.release(sheet_handle); -console.log('smoke ok'); diff --git a/experiments/css/demo/styles.css b/experiments/css/demo/styles.css deleted file mode 100644 index b905a24..0000000 --- a/experiments/css/demo/styles.css +++ /dev/null @@ -1,109 +0,0 @@ -:root { - color-scheme: dark; - font-family: ui-monospace, SFMono-Regular, Menlo, monospace; - background: #111; - color: #eee; -} - -* { - box-sizing: border-box; -} - -body { - margin: 0; - min-height: 100vh; - background: radial-gradient(circle at top right, #29352e, #111 48%); -} - -header { - max-width: 1100px; - margin: 0 auto; - padding: 48px 24px 8px; -} - -h1 { - margin: 0 0 8px; - font-size: 1.6rem; - letter-spacing: -0.02em; -} - -header p { - margin: 0; - max-width: 640px; - color: #a8b0aa; - line-height: 1.6; -} - -main { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px; - max-width: 1100px; - margin: 24px auto 64px; - padding: 0 24px; -} - -.editor, -.result { - display: grid; - align-content: start; - gap: 10px; - min-width: 0; -} - -label { - color: #93d4a4; - font-size: 0.75rem; - letter-spacing: 0.12em; - text-transform: uppercase; -} - -textarea, -pre, -input, -output { - font: inherit; - line-height: 1.55; -} - -textarea, -pre { - width: 100%; - min-height: 360px; - margin: 0; - padding: 18px; - overflow: auto; - border: 1px solid #334139; - border-radius: 8px; - background: #151b17; - color: #e8f3e9; -} - -textarea { - resize: vertical; - outline: none; -} - -textarea:focus { - border-color: #93d4a4; - box-shadow: 0 0 0 3px #93d4a422; -} - -input { - width: 140px; - padding: 6px 10px; - border: 1px solid #334139; - border-radius: 6px; - background: #151b17; - color: #e8f3e9; -} - -output { - color: #a8b0aa; -} - -@media (max-width: 720px) { - main { - grid-template-columns: 1fr; - } -} diff --git a/experiments/css/tasks/bundle-wasm.ts b/experiments/css/tasks/bundle-wasm.ts deleted file mode 100644 index 18b8d45..0000000 --- a/experiments/css/tasks/bundle-wasm.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { brotliCompressSync, constants } from "node:zlib"; - -const Z85 = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; - -function encodeZ85(data: Uint8Array): string { - let padLen = (4 - (data.length % 4)) % 4; - let src = data; - if (padLen > 0) { - src = new Uint8Array(data.length + padLen); - src.set(data); - } - let out: string[] = []; - for (let i = 0; i < src.length; i += 4) { - let v = src[i] * 16777216 + - src[i + 1] * 65536 + - src[i + 2] * 256 + - src[i + 3]; - out.push( - Z85[Math.floor(v / 52200625)], - Z85[Math.floor(v / 614125) % 85], - Z85[Math.floor(v / 7225) % 85], - Z85[Math.floor(v / 85) % 85], - Z85[v % 85], - ); - } - return out.join(""); -} - -const wasm = await Deno.readFile("demo/bombshell_css_wasm.wasm"); - -const compressed = new Uint8Array( - brotliCompressSync(wasm, { - params: { - [constants.BROTLI_PARAM_QUALITY]: 11, - [constants.BROTLI_PARAM_SIZE_HINT]: wasm.length, - [constants.BROTLI_PARAM_LGWIN]: 24, - }, - }), -); - -const z85 = encodeZ85(compressed); - -// The decoder receives the pre-padding module length, so the zero bytes the -// Z85 alignment pad contributes never reach WebAssembly.compile. -// Decoding uses division instead of >>> to avoid 32-bit truncation on -// values near 0xFFFFFFFF. -const source = `import{brotliDecompressSync}from"node:zlib"; -const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; -const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; -// the table lookups are non-null for well-formed z85 input; the bounds -// checks below handle a short tail, so the assertions carry no risk -function d(s:string,n:number){const b=new Uint8Array(n);let o=0;for(let i=0;i .title { font-weight: bold } } - @media (min-width: 24em) { - @container panel (inline-size > 20em) { - .card { border-color: var(--brand) } - } - } -`; - -const { ir, diagnostics } = component.compile(css, 2); -console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); -if (!ir) { - throw new Error('compile returned no IR'); -} -console.log('ir bytes:', ir.byteLength); -const sheet_handle = component.prepare(ir); -if (sheet_handle === null) { - throw new Error('prepare rejected the IR'); -} - -const sheet = decodeIr(ir); -console.log('ir version:', sheet.version); -for (const rule of sheet.rules) { - console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); -} - -const tree = [ - { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, - { tag: 'card', classes: ['card'] }, - { tag: 'title', classes: ['title'] }, -]; -const parents = [NO_PARENT, 0, 1]; - -const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; - -const perNode = []; -for (const target of tree.keys()) { - perNode.push(component.compute(sheet_handle, tree, parents, target, env)); -} -const whole = component.computeTree(sheet_handle, tree, parents, env); -if (JSON.stringify(perNode) !== JSON.stringify(whole)) { - throw new Error('computeTree disagrees with compute'); -} -if (!whole[1].some(([name, value]) => name === 'border-color' && value === 'oklch(70% .2 40)')) { - throw new Error(`container query did not cascade: ${JSON.stringify(whole[1])}`); -} - -const other = component.compile('.x { color: blue }', 2); -const other_handle = component.prepare(other.ir); -component.compute(other_handle, tree, parents, 2, env); -component.release(other_handle); - -function memoryPages() { - return component.raw.memory.buffer.byteLength; -} -for (let i = 0; i < 500; i++) { - component.compute(sheet_handle, tree, parents, 2, env); -} -if (memoryPages() !== memoryPages()) throw new Error('unstable'); -const pagesAfterFrames = memoryPages(); -for (let i = 0; i < 500; i++) { - for (const target of tree.keys()) { - component.compute(sheet_handle, tree, parents, target, env); - } - component.computeTree(sheet_handle, tree, parents, env); -} -const growth = memoryPages() - pagesAfterFrames; -console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); -if (growth > 0) { - throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); -} -component.release(sheet_handle); -console.log('bundle ok'); From 46d5fe0168234b0a20937043e1ff350d287ec8c7 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:16:40 -0400 Subject: [PATCH 3/9] Add @bomb.sh/css JS package exposing the bindings and embedded runtime --- experiments/css/package.json | 40 ++ experiments/css/scripts/bundle-wasm.mjs | 79 ++++ experiments/css/smoke.mjs | 137 +++++++ experiments/css/src/abi.js | 478 ++++++++++++++++++++++++ experiments/css/src/index.js | 19 + experiments/css/src/wasm.js | 8 + pnpm-lock.yaml | 1 + 7 files changed, 762 insertions(+) create mode 100644 experiments/css/package.json create mode 100644 experiments/css/scripts/bundle-wasm.mjs create mode 100644 experiments/css/smoke.mjs create mode 100644 experiments/css/src/abi.js create mode 100644 experiments/css/src/index.js create mode 100644 experiments/css/src/wasm.js diff --git a/experiments/css/package.json b/experiments/css/package.json new file mode 100644 index 0000000..a37dfaf --- /dev/null +++ b/experiments/css/package.json @@ -0,0 +1,40 @@ +{ + "name": "@bomb.sh/css", + "version": "0.0.0", + "description": "The Bombshell CSS engine: compiler front-end and cascade runtime as a freestanding wasm component, with canonical-ABI bindings and the embedded artifact", + "license": "MIT", + "author": { + "name": "Bombshell", + "email": "oss@bomb.sh", + "url": "https://bomb.sh" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bombshell-dev/playground.git", + "directory": "experiments/css" + }, + "type": "module", + "exports": { + ".": "./src/index.js", + "./package.json": "./package.json" + }, + "files": [ + "src" + ], + "scripts": { + "test": "node smoke.mjs", + "bundle": "node scripts/bundle-wasm.mjs" + }, + "devEngines": { + "packageManager": { + "name": "pnpm", + "version": "10.7.0", + "onFail": "error" + }, + "runtime": { + "name": "node", + "version": "22.14.0", + "onFail": "error" + } + } +} diff --git a/experiments/css/scripts/bundle-wasm.mjs b/experiments/css/scripts/bundle-wasm.mjs new file mode 100644 index 0000000..23d818d --- /dev/null +++ b/experiments/css/scripts/bundle-wasm.mjs @@ -0,0 +1,79 @@ +// Encode a built wasm module into `src/wasm.js`: brotli at maximum quality, +// Z85-encoded into a self-contained module that embeds its own decoder and +// exports `compiled`, a ready `WebAssembly.Module`. Same bundle-friendly +// scheme the tty repo uses for its embedded artifacts. +// +// The optimized freestanding artifact needs nightly (`-Z build-std`, +// immediate-abort panics) plus Binaryen; see the css-compiler workspace +// README in the css repository history for the full recipe: +// +// cargo +nightly build -Z build-std=std,panic_abort \ +// -p bombshell-css-wasm --target wasm32-unknown-unknown --release +// wasm-opt -Oz --enable-bulk-memory --enable-reference-types \ +// --enable-nontrapping-float-to-int --enable-sign-ext \ +// --enable-mutable-globals --enable-multivalue --enable-tail-call \ +// -o bombshell_css.wasm target/wasm32-unknown-unknown/release/bombshell_css_wasm.wasm +// +// Usage: node scripts/bundle-wasm.mjs + +import { brotliCompressSync, constants } from "node:zlib"; +import { readFileSync, writeFileSync } from "node:fs"; + +const Z85 = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; + +function encodeZ85(data) { + let padLen = (4 - (data.length % 4)) % 4; + let src = data; + if (padLen > 0) { + src = new Uint8Array(data.length + padLen); + src.set(data); + } + let out = []; + for (let i = 0; i < src.length; i += 4) { + let v = src[i] * 16777216 + src[i + 1] * 65536 + src[i + 2] * 256 + src[i + 3]; + out.push( + Z85[Math.floor(v / 52200625)], + Z85[Math.floor(v / 614125) % 85], + Z85[Math.floor(v / 7225) % 85], + Z85[Math.floor(v / 85) % 85], + Z85[v % 85], + ); + } + return out.join(""); +} + +const wasm = readFileSync(process.argv[2]); + +const compressed = new Uint8Array( + brotliCompressSync(wasm, { + params: { + [constants.BROTLI_PARAM_QUALITY]: 11, + [constants.BROTLI_PARAM_SIZE_HINT]: wasm.length, + [constants.BROTLI_PARAM_LGWIN]: 24, + }, + }), +); + +const z85 = encodeZ85(compressed); + +// The decoder receives the pre-padding module length, so the zero bytes the +// Z85 alignment pad contributes never reach WebAssembly.compile. +// Decoding uses division instead of >>> to avoid 32-bit truncation on +// values near 0xFFFFFFFF. +const source = `import{brotliDecompressSync}from"node:zlib"; +const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; +const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; +// the table lookups are non-null for well-formed z85 input; the bounds +// checks below handle a short tail, so the assertions carry no risk +function d(s,n){const b=new Uint8Array(n);let o=0;for(let i=0;i .title { font-weight: bold } } + @media (min-width: 24em) { + @container panel (inline-size > 20em) { + .card { border-color: var(--brand) } + } + } +`; + +const { ir, diagnostics } = component.compile(css, 2); +console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); +if (!ir) { + throw new Error('compile returned no IR'); +} +console.log('ir bytes:', ir.byteLength); +const sheet_handle = component.prepare(ir); +if (sheet_handle === null) { + throw new Error('prepare rejected the IR'); +} + +const sheet = decodeIr(ir); +console.log('ir version:', sheet.version); +for (const rule of sheet.rules) { + console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); +} + +const tree = [ + { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, // container + { tag: 'card', classes: ['card'] }, + { tag: 'title', classes: ['title'] }, +]; +const parents = [NO_PARENT, 0, 1]; + +// Node 0 as a named container via container-type comes from computed styles; +// this fixture relies on the width. Compute each node with a dark viewport. +const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; +for (const target of [0, 1, 2]) { + const computed = component.compute(sheet_handle, tree, parents, target, env); + console.log(`node ${target}:`, JSON.stringify(computed)); +} + +// Frame loop: `compute` runs per animation frame in real hosts, so its +// memory must not grow over time. The component caches the parsed sheet for +// identical IR bytes; a growing trace means the cache is leaking. +function memoryPages() { + return component.raw.memory.buffer.byteLength; +} +// The tree API must agree with the per-node API. +const perNode = []; +for (const target of tree.keys()) { + perNode.push(component.compute(sheet_handle, tree, parents, target, env)); +} +const whole = component.computeTree(sheet_handle, tree, parents, env); +if (JSON.stringify(perNode) !== JSON.stringify(whole)) { + throw new Error('computeTree disagrees with compute'); +} + +// Re-prepare + release exercises the slot lifecycle before the loop. +const other = component.compile('.x { color: blue }', 2); +const other_handle = component.prepare(other.ir); +component.compute(other_handle, tree, parents, 2, env); +component.release(other_handle); +for (let i = 0; i < 500; i++) { + component.compute(sheet_handle, tree, parents, 2, env); +} +if (memoryPages() !== memoryPages()) throw new Error('unstable'); +const pagesAfterFrames = memoryPages(); +for (let i = 0; i < 500; i++) { + for (const target of tree.keys()) { + component.compute(sheet_handle, tree, parents, target, env); + } + component.computeTree(sheet_handle, tree, parents, env); +} +const growth = memoryPages() - pagesAfterFrames; +console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); +if (growth > 0) { + throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); +} + +// Option-returning calls after compute-tree: the shared return area holds a +// pointer from compute-tree's lowering, so the option discriminant's padding +// bytes are no longer zero. Reading the disc as anything wider than one byte +// misreads these results as absent. +const hot = component.compile('.card { color: #fff }', 2); +if (hot.ir === null) { + throw new Error('compile after compute-tree reported no IR'); +} +const hotHandle = component.prepare(hot.ir); +if (hotHandle === null) { + throw new Error('prepare after compute-tree rejected valid IR'); +} +component.release(hotHandle); + +// The rest of the poison matrix: re-preparing the ORIGINAL IR bytes… +const rePrepare = component.prepare(ir); +if (rePrepare === null) { + throw new Error('re-prepare of the existing IR rejected valid bytes'); +} +// …and computing on a sheet adopted after compute-tree. +const adopted = component.compile('.card { color: #0f0 }', 2); +if (adopted.ir === null) { + throw new Error('compile after compute-tree reported no IR'); +} +const adoptedHandle = component.prepare(adopted.ir); +if (adoptedHandle === null) { + throw new Error('prepare after compute-tree rejected valid IR'); +} +const afterPoison = component.computeTree(adoptedHandle, tree, parents, env); +if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { + throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); +} +component.release(rePrepare); +component.release(adoptedHandle); + +// Diagnostics surface through the ABI: an invalid color warns and the rest +// of the rule recovers. +const warned = component.compile('.card { color: notacolor; padding: 4px }', 2); +if ( + warned.ir === null || + !warned.diagnostics.some( + (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), + ) +) { + throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); +} + +component.release(sheet_handle); +console.log('smoke ok'); diff --git a/experiments/css/src/abi.js b/experiments/css/src/abi.js new file mode 100644 index 0000000..cc5873d --- /dev/null +++ b/experiments/css/src/abi.js @@ -0,0 +1,478 @@ +// Canonical ABI bindings for the bombshell:css component (wasm32). +// +// All in-memory layouts follow the component-model canonical ABI. Strings +// and lists are (ptr, len) pairs pointing at wasm memory; string bytes are +// copied into linear memory via `cabi_realloc`. + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); + +export const NO_PARENT = 0xffffffff; + +export async function loadCssComponent(source) { + const bytes = source instanceof Uint8Array ? source : new Uint8Array(await source.arrayBuffer()); + const instance = (await WebAssembly.instantiate(bytes, {})).instance; + return createComponent(instance); +} + +export function loadCssComponentFromModule(module) { + return createComponent(new WebAssembly.Instance(module, {})); +} + +function createComponent(instance) { + const exports = instance.exports; + let memory = new Uint8Array(exports.memory.buffer); + let view = new DataView(exports.memory.buffer); + const realloc = exports.cabi_realloc; + const compileFn = exports['bombshell:css/stylesheet@0.3.0#compile']; + const computeFn = exports['bombshell:css/stylesheet@0.3.0#compute']; + const prepareFn = exports['bombshell:css/stylesheet@0.3.0#prepare']; + const releaseFn = exports['bombshell:css/stylesheet@0.3.0#release']; + const computeTreeFn = exports['bombshell:css/stylesheet@0.3.0#compute-tree']; + const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree']; + const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compile']; + const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute']; + function refresh() { + memory = new Uint8Array(exports.memory.buffer); + view = new DataView(exports.memory.buffer); + } + + // Host allocations use `cabi_realloc`: wit-bindgen lifts string/list + // arguments zero-copy (it takes ownership of the buffer and frees it with + // its own allocator), so inputs must come from the allocator the callee + // will free with, and must NOT be freed host-side. Callee-owned return + // areas are released by the `cabi_post_*` hooks after the host reads. + function allocate(bytes) { + const pointer = realloc(0, 0, 1, bytes.byteLength); + refresh(); + memory.set(bytes, pointer); + return pointer; + } + + function writeString(text) { + return allocate(encoder.encode(text)); + } + + function readString(pointer, length) { + return decoder.decode(memory.subarray(pointer, pointer + length)); + } + + function u32(pointer) { + return view.getUint32(pointer, true); + } + + function f32(pointer) { + return view.getFloat32(pointer, true); + } + + function stringPointer(text) { + const bytes = encoder.encode(text); + return { pointer: allocate(bytes), length: bytes.byteLength }; + } + + function stringListPointer(items) { + if (items.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = items.map(stringPointer); + const pointer = realloc(0, 0, 4, entries.length * 8); + refresh(); + entries.forEach((entry, i) => { + view.setUint32(pointer + i * 8, entry.pointer, true); + view.setUint32(pointer + i * 8 + 4, entry.length, true); + }); + return { pointer, count: entries.length }; + } + + function attributeListPointer(attributes) { + if (attributes.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = attributes.map((attribute) => ({ + name: stringPointer(attribute.name), + value: stringPointer(attribute.value), + })); + const pointer = realloc(0, 0, 4, entries.length * 16); + refresh(); + entries.forEach((entry, i) => { + const base = pointer + i * 16; + view.setUint32(base, entry.name.pointer, true); + view.setUint32(base + 4, entry.name.length, true); + view.setUint32(base + 8, entry.value.pointer, true); + view.setUint32(base + 12, entry.value.length, true); + }); + return { pointer, count: entries.length }; + } + + function compile(css, origin = 2) { + const bytes = encoder.encode(css); + const cssPointer = allocate(bytes); + const result = compileFn(cssPointer, bytes.byteLength, origin); + refresh(); + // compiled-sheet: option> (disc u8@0, ptr@4, len@8) + // + list (ptr@12, len@16) + // The option discriminant is one byte; bytes 1..3 are padding the + // canonical ABI leaves undefined, so they must not leak into the check. + const hasIr = view.getUint8(result) === 1; + const irPointer = u32(result + 4); + const irLength = u32(result + 8); + const diagnosticsPointer = u32(result + 12); + const diagnosticsCount = u32(result + 16); + const ir = hasIr ? memory.slice(irPointer, irPointer + irLength) : null; + const diagnostics = []; + for (let i = 0; i < diagnosticsCount; i++) { + // diagnostic: severity u8@0, code (ptr@4, len@8), message (ptr@12, len@16) + const base = diagnosticsPointer + i * 16; + diagnostics.push({ + severity: view.getUint8(base), + code: readString(u32(base + 4), u32(base + 8)), + message: readString(u32(base + 12), u32(base + 16)), + }); + } + // The callee-allocated return area (and the strings it points to) is + // released by the post-return hook, after the host has finished reading. + postCompile(result); + return { ir, diagnostics }; + } + + /// Decode and parse IR once; returns a handle for `compute`. + function prepare(ir) { + const irBytes = ir instanceof Uint8Array ? ir : new Uint8Array(ir); + const irPointer = allocate(irBytes); + const result = prepareFn(irPointer, irBytes.byteLength); + refresh(); + // option: disc u8@0, value@4 — discriminant is one byte; the + // canonical ABI leaves bytes 1..3 undefined, so read only the byte. + return view.getUint8(result) === 1 ? u32(result + 4) : null; + } + + /// Resolve computed styles for every node in one pass. Returns one + /// declaration list per node, index-aligned with `nodes`. + function computeTree(sheet, nodes, parents, env) { + const stride = 56; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + }); + const nodesPointer = allocate(data); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeTreeFn( + sheet, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list>: (ptr@0, len@4); each entry is + // (ptr@0, len@4) over 16-byte name/value pairs. + const outer = u32(result); + const outerCount = u32(result + 4); + const perNode = []; + for (let i = 0; i < outerCount; i++) { + const entryBase = outer + i * 8; + const pointer = u32(entryBase); + const count = u32(entryBase + 4); + const declarations = []; + for (let j = 0; j < count; j++) { + const base = pointer + j * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + perNode.push(declarations); + } + postComputeTree(result); + return perNode; + } + + function release(sheet) { + releaseFn(sheet); + } + + function compute(sheet, nodes, parents, target, env) { + // node: tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), + // classes list (ptr@20, len@24), attributes list + // (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), + // height option (disc@48, f32@52) — 56 bytes per node. + const stride = 56; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + }); + const nodesPointer = allocate(data); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeFn( + sheet, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + target, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list: (ptr@0, len@4) + const pointer = u32(result); + const count = u32(result + 4); + const declarations = []; + for (let i = 0; i < count; i++) { + // computed-property: name (ptr@0, len@4), value (ptr@8, len@12) + const base = pointer + i * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + postCompute(result); + return declarations; + } + + return { compile, prepare, compute, computeTree, release, raw: exports }; +} + +// --------------------------------------------------------------------------- +// Postcard reader for CompiledStylesheet (IR_VERSION 2) +// --------------------------------------------------------------------------- + +export function decodeIr(bytes) { + const reader = new IrReader(bytes); + return { + version: reader.varint(), + origin: reader.byte(), + rules: reader.vec(() => reader.rule()), + layerOrder: reader.vec(() => reader.stringVec()), + keyframes: reader.vec(() => ({ name: reader.string(), body: reader.string() })), + opaque: reader.vec(() => ({ text: reader.string() })), + warnings: reader.vec(() => reader.diagnostic()), + }; +} + +class IrReader { + constructor(bytes) { + this.bytes = bytes; + this.cursor = 0; + this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + } + + byte() { + return this.bytes[this.cursor++]; + } + + varint() { + let value = 0; + let shift = 0; + for (;;) { + const byte = this.bytes[this.cursor++]; + value |= (byte & 0x7f) << shift; + if ((byte & 0x80) === 0) { + break; + } + shift += 7; + } + return value >>> 0; + } + + zigzag() { + const n = this.varint(); + return (n >>> 1) ^ -(n & 1); + } + + string() { + const length = this.varint(); + const text = decoder.decode(this.bytes.subarray(this.cursor, this.cursor + length)); + this.cursor += length; + return text; + } + + stringVec() { + return this.vec(() => this.string()); + } + + vec(read) { + const count = this.varint(); + const items = []; + for (let i = 0; i < count; i++) { + items.push(read()); + } + return items; + } + + diagnostic() { + return { severity: this.byte(), code: this.string(), message: this.string() }; + } + + rule() { + return { + selector: this.string(), + specificity: this.varint(), + layer: this.option(() => this.stringVec()), + media: this.vec(() => this.mediaCondition()), + containers: this.vec(() => ({ + name: this.option(() => this.string()), + condition: this.containerCondition(), + })), + supports: this.vec(() => this.supportsCondition()), + declarations: this.vec(() => ({ + name: this.string(), + value: this.string(), + important: this.byte() === 1, + })), + order: this.varint(), + }; + } + + option(read) { + if (this.byte() === 0) { + return null; + } + return read(); + } + + mediaCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'feature', feature: this.feature() }; + case 1: return { type: 'not', inner: this.mediaCondition() }; + case 2: return { type: 'and', conditions: this.vec(() => this.mediaCondition()) }; + case 3: return { type: 'or', conditions: this.vec(() => this.mediaCondition()) }; + default: return { type: 'unknown' }; + } + } + + feature() { + const name = this.string(); + const kind = this.varint(); + switch (kind) { + case 0: return { name, kind: { type: 'plain', value: this.featureValue() } }; + case 1: return { name, kind: { type: 'boolean' } }; + case 2: return { name, kind: { type: 'range', cmp: this.varint(), value: this.featureValue() } }; + case 3: return { + name, + kind: { + type: 'interval', + start: this.featureValue(), + startCmp: this.varint(), + end: this.featureValue(), + endCmp: this.varint(), + }, + }; + default: throw new Error(`unknown feature kind ${kind}`); + } + } + + featureValue() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'px', value: this.f32() }; + case 1: return { type: 'length', value: this.f32(), unit: this.string() }; + case 2: return { type: 'number', value: this.f32() }; + case 3: return { type: 'integer', value: this.zigzag() }; + case 4: return { type: 'boolean', value: this.byte() === 1 }; + case 5: return { type: 'dppx', value: this.f32() }; + case 6: return { type: 'ratio', a: this.f32(), b: this.f32() }; + case 7: return { type: 'ident', value: this.string() }; + default: return { type: 'unknown' }; + } + } + + f32() { + const value = this.view.getFloat32(this.cursor, true); + this.cursor += 4; + return value; + } + + containerCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'size', feature: this.feature() }; + case 1: return { type: 'style', text: this.string() }; + case 2: return { type: 'not', inner: this.containerCondition() }; + case 3: return { type: 'and', conditions: this.vec(() => this.containerCondition()) }; + case 4: return { type: 'or', conditions: this.vec(() => this.containerCondition()) }; + default: return { type: 'unknown' }; + } + } + + supportsCondition() { + const tag = this.varint(); + switch (tag) { + case 0: return { type: 'declaration', name: this.string(), value: this.string(), supported: this.byte() === 1 }; + case 1: return { type: 'selector', text: this.string(), supported: this.byte() === 1 }; + case 2: return { type: 'not', inner: this.supportsCondition() }; + case 3: return { type: 'and', conditions: this.vec(() => this.supportsCondition()) }; + case 4: return { type: 'or', conditions: this.vec(() => this.supportsCondition()) }; + default: return { type: 'unknown' }; + } + } +} diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js new file mode 100644 index 0000000..d7bf4ff --- /dev/null +++ b/experiments/css/src/index.js @@ -0,0 +1,19 @@ +// @bomb.sh/css — bindings and runtime for the bombshell:css component. +// +// - `compiled` is the freestanding wasm module (brotli+Z85 embedded; the +// module is decoded eagerly at import and ready for synchronous +// instantiation). Regenerate with `pnpm bundle `. +// - `component` is the instantiated engine: `compile`, `prepare`, `compute`, +// `computeTree`, `release` — the WIT world `bombshell:css@0.3.0`. +// - `loadCssComponent(bytes)` / `loadCssComponentFromModule(module)` for +// hosts that ship their own artifact; `decodeIr` decodes the compiled +// stylesheet IR for inspection. +// +// The embedded artifact decodes via `node:zlib`, so it targets Node-like +// runtimes; browser hosts load the raw module and use `loadCssComponent`. +import { compiled } from "./wasm.js"; +import { loadCssComponentFromModule } from "./abi.js"; +export { NO_PARENT, loadCssComponent, loadCssComponentFromModule, decodeIr } from "./abi.js"; +export { compiled }; + +export const component = loadCssComponentFromModule(compiled); diff --git a/experiments/css/src/wasm.js b/experiments/css/src/wasm.js new file mode 100644 index 0000000..960f593 --- /dev/null +++ b/experiments/css/src/wasm.js @@ -0,0 +1,8 @@ +import{brotliDecompressSync}from"node:zlib"; +const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; +const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; +// the table lookups are non-null for well-formed z85 input; the bounds +// checks below handle a short tail, so the assertions carry no risk +function d(s,n){const b=new Uint8Array(n);let o=0;for(let i=0;ig-nnPfvLR2NT5@-*!6Xe=A1?f?:yr8}FQ1PHl%}wYb^08Q8*(kSFSK/zn-?eBqOhK#FiA!H1B.9XRICi$X/-(5hIdhi4q7P:1nvU/R4lfEV6=fTgEX2Bm^uIn^*e+N^aKtP=6R>GN}(FBMUs:)y1h9&3}IkqFLDt8W/1/tZWS&pBup%BDV5JcQf(GW.t96i]WT!>a9B+RHTUD#T1pUONSy$+^IOvwGcEIE-j/%Gzs{/F1#(p(ycAb>Xk=4-Ua(}tcBlW[%s@i6PPzE#}SX*q=q]U{[&lK^l+M.mSQUHZHn?MJ)lGV!O25H1Q:V#/Lpq6#WmXETE8g&G+@K5NA{OYI/KG3j&h[Gzam>IkW=YeaJ%Pu/ONI=@+{oUU!uIq3dn)(sZ^8GV@#+NB6)Jxf&7r]0A]$gnQJq)%?uX?*nCIw8s6O0/P5KxF}?qoT7qFq9)vK+MUHhfg0Qp+uAUDIRMn!DbfR!Fgr*>Rf7x{t9VO8}DqJVjEQtn%h[U[{Wf@jZ>W:pr-L6Bi!I3VBGiVGZC!tFJ^Xg4X#c=XT$v=2tI9DOO4sOL0+kq&7nkviS]ZXoyU]&F>kwj5sEpTZ2}Ms>EQD6ZJQELtIX{1(p9%M(z1fBXTG43HH9VfOc*n%GFL}7W.xZ)@3(*o@LgZoz!QAMN%8^=eq*wyWBgXFZ877nLse+RTeL>.-6P=HdWZFpoZM5pTDEA!^tsB3h/d<-hvEeN/Jo@BH9D45Lyj}=^6a{ugm4jt1]jgun!uXL-b&UGg!pqJ%9emC*B%rc*q1At-%9[yQ!+l-KE&[!rzgK#JMxTnFS.4wd(8-wh06?5PYm$QY^@t2pD*=)kC/76K@#V^^@%}}-2-]/kL*PL5/^bTq04qyUPP{SC}&D#EL9zIyW9<8S^x@$IE0{Ycb}7y@)9w{ul5>e@!uZKB1GTGL2W1fi@)8h$G#Rk}B)[Ztz=q1fGCkCXoXhupwyq}/ID9oZNkeF!L21h%7&V/FxJ7qG0V(=eBQc1ZL#gQ?yj-]Kjs(Z]6=hG0R4AfAu@tAIGek-N7&.k?c{G=f@t)@UAl3[7dL(d3KhT/WSnkKiRO9Wq{]WeZ6R5/=^K[bE]R}tyfJET{fqB:359M=EZyC5R(Oiw}C44F^2I-ruJN2YN-+Fik@u67OIoL0.Ii>H>0HZxwl-].H9cW@q6sTFr7*78OC9FJTe@qVW6VWHkWj@i*g>ffo=zJ5VC&ilsAw+NBg4h#6kmjvY+R[Cd]iXy6StF3J$<5U4910H=lS?&%BgCd5juHdj%b&dII6<3^Ju}yQStwO!xhpyhAgT6jHY0:!UpPDhW0h]f{GP(tuhhvI=ZYl(xmPvR&z8z-A5KUq*FJ:1h[So@Kx5r+}WCW}Tu00z^7Q.!]]ud>+2B-=7v{)tmUz%Px(i9ZxMzLDcg4si<3s2H=tcPm=w@W:^XM{MpIA8UGauVEu.}gRJi:]1k#A$hNus:Vhos>H4W5WIE9v$D)/6.g#DVU3xvQC(@}0eht5=LA7vFHc/)eB/$Fo9&LWMDLnC<*.KFFy}I?Qk85O0-yuRxnz5CTKP.p>:D!iduI3yuLbXO^ekN&%dYGq&[mW7Ju[+{3@2sdkMN056b4WjObEBrBp>eKlV/qBsq4egL0c+zAC8K4?t?pHdiTzJ{wFy9!XJkoa0x}GKG?$H+epDE@K^XTgCEVXu0m1bjFIY!1[Cv0B5r3mu(1j60)NyF6e9^5f4:CguGLsvAb?5P.65!eDBSJ2(PM<-(lorqImfp=x6f+Xf85{q14iDe$(2xr]9q=NQdc&&cbBsuWl>l/o^h1@@f%s[A7tey9M6TEnMnnzN%RY)YUBaLmPdhuFxIQfzge>XvKR}8K5Z&UI/uWN@vky8-?pOwO:=?2MvSgEcnEuZu8u5#S-4{J35v-on74vXSYcVn9}R!as%7Nq0KFZI>cH0bOiSU?@*N+E>7{2c(Eri2Y[])G6h8!x)hCR.yuY/QAx(PdId.}GBp^Spu+>!mOpnHWPsX=.?!>NViZ?Wrh]SKe{S]sEv>Wh=5g1Ly[#:Xov?GOa)R8$6BXjR*yCk#:hGOxN!BD51r.yCL0NTM^)+xZ#h1n@:n8E53+%H)4xjg-6/AI9F*yBO.aDBj@-11Qt@8/B3vdN%lIvL^D1T*K){2yUxpC1ndlysuE-o3Q1CE8/mJjX32c7@[oL^fUjlzs&#kSLeB26T72hg<&F+GNLXVLQKKchq(8gl9h6ZZ5?F2:/SQ.!3{>E3ooOr0k{MT42:cT!2!9(PONLt%bWBVD@{b*vLAMjNw[hcov47bdf:#/)au=i94uq{qlnQ<+?O(gq]MxFLikMqC@tL=@Jzpr^3smgoebxt?=vgz3qf8c(N{@a:WlYp}e/:5/R6F2z.V]eOtUgJfu]iG/OPc>@h=:B@C0q?SCE-mYxrCk+Tzbv!5Q!s71Rp:7!ntzTkOgHq6DeI]dPL8MK-T^Lmadc2b4zhTrT)Nw&:)KKLPIwa-A=&+C=n6]%Vzvh3=)R#]*DBb&:x9G3g+i4*!%k9&}H!ew#X}SW38c8j5uOLgvg/}]]9vih^kuGc*rc9MCYk(G<^f=u5bGJa3F*Z>Q)m>6EblM8q5yZ]cx&D*t-U2[.opUg&42hImwI8L*5jRZHgcQ(4j33nM}&8*&(D2k9UKon{H4?>E+REu/)HFhGNcfP{W+A+HJ5VV-^o80jBEM79/VX0q)!Uj^9V8m8Ya/Yp5[1cxCj<0wUqZZu2{Z6(?#=c!UhR>4Lzo?RE$2ilO.)AgDxrp5g-*O-Bhqp*v>a/c:u!Ta#SB&U8h^D+OKI<-$N5E8oiB4Bwvu9}7822nmG19VFf/bGJ:UJi$hXA.6fL5bZDw)<2VG0xP0]k#Z.*o+Q]qvpDC>1A[WyiFF#WT!}VvlNFD:m45=9!&lZ{hfK8pY5}F0Xr!^6/Rne+>CG3)nc:U^)z67@VG?hT{aj%g*-sKGT%j-aq[V@VIV)37-.}?.3.*4O(6/+Vrj=R@/T(rz3{S(]jRqLgrNUH{Wg!w)qlX/La$-TjKy9aJDVSy*Z>}ma}YaYGAbB]2d9IbaEd=gBUm>UH3}s[l>/sp@b55q(U94&(fCOzw)+Wn^KtpIWfPEl3O#lcWd#koFHGWnK?RIufJ=]ar>I%:1>ek4LdY8F@]W4^?S^HG8=HAGThsJDynnwL@{l6]Hq$9ukGjyfm6&JG5U+rI6Nu832*h}Q(}Dq!7%s2.tfk/Ogb&VFy.:gkiUK*bnm&^rkH!bS2m>TYjv^*B=nkl47DtDgM9%t?!4sNFe/z&d+XqK06rZ].}f2o}]p!6WYl1G*^@*Ks/ttXA2cr3N&-hrXdjG=DqzR2DDH7OD@iydt@1dMPC#>Y-*inisQWnL92(TMpj>fds^1JmP2Gi+Cr#aQJT}OzEmI8t+m7QC7I3RCh]sBh)iFn>*>ImG{5cDNCZoBj(QG2KKo0=%l7IDwj]k67UnzQI.8Xu1aD?53%EDaBsV[W3Uh9A@C0gm.G<[5W{8s}*pCrptq3cxGFvRgZi3>Hvk33n@O[3-)6*oN4:tv6/7@iJXKL6yk+<(4WnF(c*8J-A0??h&&NmX&gHIYqQVaqd$D0V{K>tPxdwrmX@F?s8&$q-(u:0XGHv+C2e[V.l4mqY#Bh!Xim4YS3M{u^@@j=)k->5c#bj5UTviq(261K?0o/ty1{:Q!%Ck/Sn^0cos11CyYue6ViGpHnpV.cC8?#i?nffx<(!3/UF1BA43DO.{ZXmnIDMoIv!=*8@oNT0-=E$cvlTx{0#YsR)/MkI1pSTEnN?:9bCgEmXVV5y*JkX{Pvi32cP3zJ=tQ[v{Bcbt>mpcIH2#ZeVm(/5Lce.PP&hxwi$I2kQ::Sl*BSj%S6>>$l-PLF&X3Dw^]}H!0Pwtp2Lo-IXLAGn6V{Kau8c]Y.hWo6OpKvHX((3Q=>{7=!i4@OgLk2/v*S!ab7UPU-WsBGvKAA)NthFy:3]I?Ta)W]s!-XokT=&Dir-K=.Pt6MzSH(c62V[8Tg!O?qvPo#HM(Tj&dNZ-v*L*HNXytLUd10TB$QsB7cB^oNIz=eBgl]w+DGdJw#fla7dKPZd.ZOSE?RHm5){M^O^w4F^B}SAOL=9xD?F/#utH-!d!qh?o!2aa1U9{a?/#OP:fmwZ:G+$tkyG(ZC?fHzLJb1*:@:[tgTpN+cQI5win7ef}C45RSs7dmq2B)zMsR:)t9$C=!k!Kmhu)MW2(R{^DXL.Pr8JtfC+A!]wMkVJEXwQWMOZd(q>@zG/+x:E-^7rtq/X#vX]@+3-mBbVyzFo9l))7BhW1aOO}oL&c@IG!m7i>/JV5.my10NjBw&IYH^x9ivhRWmAJR&&9vd8zH]#=[VoTva1Wr&sNz-XD7p*YpZq.QWzCVNTq#uQx=xw2}Lwr4Bx4%oeLKXDsc*#y:t+0.z=EwIptz#rxfkw8oRAi+1dU0YaN*tnjgv+4Vh{fEXxn*DRM?RZX*mv-LC7b@u4WsM>}a4Uf/U63%0&K+==>HU767yoy]N9(@&@JFZR$/pWK>P^(i5e4Rj+aiwKU^to7oM+WDbZtl]V8RViP#GU&<5J22SOCk}iPMs(&rhx6uj.w6XC.=izBfGZHF+tyP96I^>zoIh$u-PCo>+>}H7Pdv?0Y?nDuW/MJHFAprTB0Ly$$rU#iCGU&/26!XXvA94U4Lf4g5cPadY5ITTD>jZX.ZwT[.!OJ=yXUt+}rg}0u+l[{nZ&YR#ml3{7CosM=r7?O4+st/BlW^GN-BMDBv(7zEPzUn385f}tpMF&dh83!kdbBU.zxO6M-+A^BeOXuaZ:lx.[-1yad]LNG)-Z2-#>)8{/AkSxz>>:hs4hz[/:Ij#MAVt^JelUO>4sq1EH8l.qKBVGZ48+CZ}uL(>2YvQ9eoZ%dEHEjvyAMv)%:NL{sAlES?6&*po.geXa+.wl.M[0ruVo+Du{A#QeUhyCj.:Ho0a:m^@:67sPZq.:Jyyn*R!e{:of0=mQwr=^[BY7bXEgv/Uzey&)TcE>k?/e7H2h9%Pq{iI%.=C?JP8Q0M}7e2XMpi.nxgtq}zhVIrd(v@gpLlg49Q#}d8&$izCyFtD&RBnIF[tsOasF[%*OEn#UIr0mS1$!L{uEHn]09+^emj<5CTmB<6!7<:4X$m{rBe}TBpl:jtnJh]k&U%i6-mpBgb=6oAM7P#/8P4%/^y0Uu*UR.Cjm.7ISdrIaSX)p*p?=kP[Wb.p!n%1(49CW@U@HdmJWaCK07:J{Nd{q&n+yID[WaavQ[+(f]@{eF5K7Iz-%A]hz*HiVo2acsMKf!*CNa+Po<^C5aeV4a2f8[KRl0^o@8C&Uh/>(T]x^9/hKlc#4Nv!ySz6(P)p@xJgW$IP>#Rt^Fn#7rolvtT7sZJCMR0i[p}emfy#F5U/QR^LGOK)WLhf@6p+?B=wsOqs+jt82r{kpx94uVE&n/imN5>%X]U!kFvy%ZkNVctG^Bm%wCsQoJJJskP)fw/.jy%z:z?.IY)O><$r^M6#5c3l&E=XKFAXem7(58Qymc{f%-JE#Q2p4^w5%439LmCjKGF^]9mfQ:5vAbA0F1(7CRs%^+t*zmdNr(xaoe}0yV1kW}./?$U[V9^Xl-+]hBp>+$.Z%YxPq1Bo)(BLN*g2&YN@q$UbV!OkM(MW?G6ylA8%zMuq(ujXfv$Ufoi6UTWXL$.&tpz0R@XD.m$LG*&vkE!V}H0rt1rghc8]m9sfig3Poi?9oAK57G>Ml0DVIc/7X!db9*NH)ipN9$1=SQbhGvy:yOJb9/1gs3Cv92@c7Rk+&Vr0M8mQpbrLdGnYEv:F0BUuKr9!NjgRH8swfrfQWJ+FV?9UP/WKJm6Pch50S=r?PCk{icbCN0G)?4=ux$%(AWs$YOf/7z5ym[#^Fmvq+Vq-AWZL&m+U[$cHGlIGgdXwMH*{Q.?:6kCXLVGrY{x5S.3GU([yu&x9jjVZd7-^4Z(ce:3UbMUfLz2cqyPBVFxZt&m}Q)XwPH?%PX3.VZJwp6v8ADVm(#yn4.^J*.7FClm])n0Vr[Xw.^Jc-/C@j*&Z3&:Q%m?B??$Lf7O5G)!)mYDQCSs4B!&P7t%&m%.Tk=P0*0IsXpFTgyScV#=X5LUk-=x2Po9Tvr&F92dvM+y>j{wjW=FQM/&zZk<*?!%*o3Icow7nl0-t6IbjWgVf&pGe1c3TB*m&8yBs3iq:&oeib=nJOe(x<25p%Q{^})0FyXfdAJ1ny{n/gfEQoXNu#?<2kbzS)0fP2m^eYs(s?Md)h{k4@#pI$G$h/:2o4l@)xv/#YiLs{0=R.*3IaAvwoaUGBOZAs2mvU3eExzUL#!P-XY>{&X]:uNz&.7qDSS0l[[(r>o($(Gt4r*-=PcG@zLR3BX}j?SYeV1d>KvYcrb(t@!}5wl-Zf!p>29Z3}^ad{t=wc+Fxj*YA2!WSJZgXM1/U0C-s=YJv7Sfu&kx5^5XP/{F4HQoqIaMV8K-GWmITEV2p97+aiUcJnn+)Oi}Hm+q$+*cVUWpL2%I.eQwsasP2gGAQ>yOjHSr!C:+Y&dEEcJbhG>Juu[qr!gZGc@5dZE1Kf>YT9XOA-U+uMNznUILH960TIa-=MVJhtX}+8?>TMM1(Ti]Ur+=2%6K%JDSrS5.sR!D}tcsJWH)C>.UumAojsfpW-SX@KQj$:3x)=@/6CkdEMOwMW*B)#?]44uqaIb{Pc^Ho2UoRrph86g<(@Lgki6.ad8MXsUY7}51&CdHOY^LdevPp)O.FGUToeoHSm^]RDRD-qd[nLD?>P@a.Up]F/XUg7@e=BnF3g8[TWCS7pJY{W3RlUycjI)RPUSc]saj2kKpZm6FmOL9FnaiKqCX2ZxPUl*C{XiMK@j8!=S6AR:&0<@g&?yzlXkp{D)BnA4d3Z:I-xHWO9^(sB>@>grC$ykgC}K2+IUV[ZP(hqPxcqFxJVQZ11[C)r&TloeZh?Z>rSnhoWnv*Z?oJq+AC4G95#ve^%ki/TAMq5jURsw5e/p.mM>eWR}Lc:A+]LK?$[GO4a1TR)]L%!gdeTdDR?=3J:CLD?efZQEHbghOjqu+yJQSWk{>/v}PX/*Hb8Nl6+%hi*#y.:HsnH1spOQ%y1A#Z:Y5vRvqNPY)1vtqR7aw6^03zO*QNg)26a/<^-hJs1e@W{V#3#vv0.[>7[=MQ^N-6{7$Cv*+iP>6:g%c0&{Cta[pg>7/n/}!(<{2^jdAH4?P(5ca>(z(&YSs?x/{Y8j6#3x.fi(dC:G+:0O6%$go?EOOT5GOj7W4[U1mEhx#.)&)1%1F%dUlpltz$ypw]g10lXp=uk.94&2$X)*YJ*+A-}?Kj6.&7X.cSO3fg<4PihPFM[Cj?Uh4s*OW@^lTGY5T*cMzdfrpT>mv8lv7{9#@dtBixz/g*r0)6DL17nx8B[SOnxhBb]sz(w{tq+BY(2xru(x8tjd7nfJ&4.hiZF$=zjU.rQe:Y$7K(C.Z=E&eB>AIGJmQnvQa%96-:]x.Q#Ns8REA:C5sM{owGw+5h]7yx9XRlw4!pAr5pSo2T&dTT7aSkr0/30)+qfWPJcYJK0s[/o2AW8H-#HtQ5EfMz?+%>C[&EK{55(}sBxPRXrjOi$GmJ}+qn.oQ@tw*Zv88PSQw(8HgkHk!owl}k&dKlq?80[OCks{Nout5r{X=ALmXTz=ptDYyOFB5tIY1Oy#w77@BJ=tHLGT$>+w1=NonncA=Ew#J+-BX>FuBQgiv0^uequfuSC^yBMZaqfPMSa0[M5XeIm{8Uuu$kg:G(dB4vs.[@NB9*w/ogXvpjqe:dQ5Gjkl)z)Vgal8zP]5#qrpwu3LJ8fu(ff4w>Rhk4fBQG]oFUOL*oEQgHLI)J]8bIqM(SGc8mqEFV5VHks^2yEh!4]64c4o4uiAzz8!j$JfRhc<{iuC!VCNIgGIz-BK0+[rY.Xo3WR1+x=}pUAIb#1.x%&fQF6-Dylh+0I5(j%25c50tP:p-TlCZmwQ=TycMF*{(trMAN4vZ5*8ip&xe4cu5yftjiVw&>kRkVZae.3zmQ/fFV4Y4+!JHAz!X6)BexsV)i?Y/(rlAIcRx1u)cF}AmWD>XyiN#)[@y>fmz[$3q^W$9[crENzu2Lrnsg.XN=N{tx}rzLFj+3t39zp2$p^Y)2BI7xi&omZ+rgPu@9?h}[.i{2Pg6=H4(%G$OV7J7i!F8->zJ/j?gc@rVW65T@@>*t5A)SGD2ijxn]*RK4j*vMIdrsKcDwi)9DW:w#wTek!tbC3rn]7&FB)nHBVM%.:Ijb{C%qFO(?TCo8snoUO0>!VlBlDeS(ft+T:Uwb$&:{Y>KQONKyvop1rlKUpEp]zL8YPc6>AQmZ#Y1Bfleo(g5$b]1jEhNz[Q+:RCGN@9Y+R{BSeLm<=hamuV!%mxB7{mg7D(RIAFoJh2K/>0J$Xd{jFyLfMA#+N6EcA{OnRyeBKtknbx#j{[i<@fkxdzuQXFN-=$*(4/%Lc2{aacz1.A}poS<4?E9BDqg(Kv=k[<0OC?f+Q:kTV<-!z%d75XG#3g2Cye*TRL2Cr/DCAfA9%bRHlrodAUE[zlnkf.b3]XdtNR%CERQ=(x!=eKcuU5B+HE7a4dc[f8akejkU0CDGRQW[a8?Y=H{:MRmlHK*0/eOlmrbc-r)bW>hK-c&5!+>A>TAeq1p^I^m8&VjuJnV+C?^ImhD+Vy4>mx!3?P>7fPK9i2HxSZKK4Pt/R:@]y/Oan0f@lG7VnI*r8C>q]KzYB7KTXUul)HdCpXscuUoz@A+HM{Emr3x:[Y}0g?.ohQFS>l6r7yK!=t{EV1s&IlZO/4r>UQ4C-HB4b^-)uGU>77X}gD45#v{RUwT3&r:X8}]r!(=yD)IJkEXp=dxaJt@Ql<[Is7hLmI}r/P}C5Yy^(If2Uh$M5!I(5s:IB3Qy9*tg?)OEg5D%6H7oPp>xvDIBZO?h6.sXSbDx9cV3kY1u99)ZYBmdc=)=r+F5x3zhuZ+w>MH(KwkNCN@qS1&(yf0IW$AGx:Z5GY6:{Z>FTy:87:Ad1dl>U35vyhuQ>rv70RLjXNYCU1q{TzmJ}k/JE4lyE4AnptW}B+Zhl]?dj@?nOqg@DDN&@zF>r8cl]&a6m7x4L{E%<*Bt]N6AMbpLXzIKs5qy:7[$*dc>g36C@P>^k:UVQ-f?GQ3T%>Tzqew3O[*mG06r6v)=vH27!n?JJlQinw1umgi#{wvB83hLEQxb?}%*]+OegN7KgYJB&02feA1:RDUcbwWCUc=TOI9V%x2Z+C=NrZvJyz==3qA4c]SE(Rxn7v{jE/F21OVIaTR2xq.+L)EFYRIl?Yvpo8pTIGgW*Ox!Z-%mXVL5dsPe4oM>UE+nv^Y*ygbOmwOGV}t+eXHdH&/J+XCd=Av+.()vR?DOz*+fOy1?X!0/W2LGU.]:&42@+4ay{99gxcldp8l.cR]02uliqRC19gYV8aCjpyJl@+yxwFkbXfN#Cg}G2hWmP=t?PiN*m@.hoyQZZYtm.fG%EK9I%&JGs2eoF4qq?&8/WZ!yjUD9.!]fE+O#g}&LnXu21(3wHbz!}5K]hlf1?h-hxWVn#8-b5qv0(0dcil6jv$y2o00Tj3T5gu[v31XM2)(dhDK**(aXb@un}zgP@V{/Jbq4UQwE:pYgXay#@qK#s5[EvNH&Sh[]aL0*BgmYzK0jSvKp3crX$W3tz]B2DLNoeX4&(=hu^q#eD6X)TpsB}()tXC<KJhch388mq/8%2]HDwNDuR(V[&V+J{R%qbFzr=v6FEV3]v72B#+O<=crwbjbGQybiO-q(Cf[%FPoM:Gs>tB=mLUeB#)mjf}l?3CM(aIv/fXDxRPKd5xY>pZR:}rF5f8LKJp{$W&$EVz*2r:4d=sx@9+j/sQI&Aca^E%iD18tDKs>SQUC4{PLC&jl7L@HGRmxEON75jCC7T@3kSP3)8Rct.Vf0p:g-@K-E.67+k>hfnL&S>n@jPgS#cLvS3N2YGN>6lu{oh72Vvhx!c76B[{w%hV8p5v.O5FP3eLV{c5lYMv??$U8e2FY9^do^Q6iMeXG&B^}qCat#-Amg}8TBqrC-$iV.9A&E-hAA&Z6G9%18(j9G]3n]^/kY5ebUZ:YI47J.b+5gjbwdvP+tyZGT=Z>2&q1#ynre@gUm6Uodn]OD1c6%g=faoNnW%OFGgOOGHT$vY#Q!#n3CS$b%J4GoKxMubn$no>9Yi>.NHK4H4ZC?i1nbTOeJK3yASGJ}IQW2q9:@ehkZZgqG%u?}nNd@Snyyp:9*/ZYa6mXABc*>G.JRuzc&oBCdZbT?.ZH7Q@hl.wq14FbS)te&zkp0of^n>n!B@@W+DH4)u46@#Az=.D$9svv?2mlQb+s@${)pJz$I{!^6zMb6l1LB4ZZIR{5eTggs.meIv%mwso69X2u-vSFrlXb34}^rpSr4eNhx&t/KJ.zc?50MaAB86wTGgs*9ELa]&g:-=ak*%.%<3gPn{IJD&fh*QTKkd6C8enmTDQ={Ts#%+LE?Z[HC/bDnXA[7hziL[U2z3#uVfM{x8fR+Gb&yrPOf>$F)6Q$0/1gy*>jY2-0E.w!SIxg2MXN}M=$]PfKKTEGzrSD?V4*5ZNqnIPa=/r#:OrGr.fWz20Pdi(EzCiElYcElHMQiySV2m[@^3XnBKh1}0PxIXi>$3^PTEkokiyt7&NUhviPsW=c(OekHD4/V9>kuR@6]oO<}4l[9:%6Mw[x=go1^Pzcinrv$3EN1HQz5*F-kwaA*$3lmgc3>fMN3G}CwGdnQ6wa*]D2}zU!hWb<]=oYV{K^l49c{FGVjqx5Ed-f/T**kh/1VNtJIj{n7O4}V9U[S6Few^>N24w#Wl&binCm)%k%p$@V/>hTTbQOpx[zp(42*Hd4&({6)OjY0Mh*R77b2fI]#fYr^{s8m79/6W2vyfC{qg2RCe{LYq^@Zbc+^((DQ582[YDlNxGQxposhoGYJM?>eZs8u3609X.VZu*@7uoCiC*7Q(C9.>Qu#=mxB+bFy)uF?sxkIx0R/tawj)Yhn5jXF#}AelA)y5nd*As@2NbHrAiHr2SIy4b{mG-cZ/>F5^L9X4NQ!#fE:OJV{q07WWH>}}KxVTP#azP>Agu89*}pEg@z]j:r?*-v{kqEva5y}!8#bFfc)X9zY{u0ygMW=W)Wf%GE(NCYFELmMiz(=cvBb1JVp[w).}k-m6&mRO{#O<>.2noEa&D.:%@!M[)IM51F+k#Skmnx8V@7Cr?a79fX0dB!I{5GWyFr).7Vu6zGbH-u[8JsA{qL#HYEFL{vz@b(hzJUIuTBM4#L{!FNwvjy?e=HP.%^tuxD@6*Gi4O}W>J:Lf:*>nze!n@L1Jd&:6(9D!YP8Oc)p2Jl]qLTe4y)S1.suAT1vb2kew>WXR]4EOGKkaS{svo[=PWrnvz*xW4-615FY@qv})oqM{gg}O0uDCiU/]HtNv%M&s/CN36qdx(2Pb+aEx0JHYRml2{zZU}kZGOfuj{%C5T(J]3nimEomA+L[6w[de{LO-8Jg{lKVVnB@{3K-.QR)w-]!-Ny.ow@vOg&I7obf[L?SWl=Q50W1[aILUnXT9g]oF1#FA*ENF8r?^(P9Y&r5NsMERbl#js^!yZP.k-sp=zYKR2}lE8ihFj*c{8&vC+m1)9sz5kYjMu9cmz0LARBcPGdCW5&[Qi>4f9#[Uhlx).Z!cFCl7N>zhy.q?]OgL}Ufku!sfH@Fl$0Ho!RBRddvi#pr.c6}Nw-fxv3v/kqGZfUZ#X6Y>bRG%hTt!s&X)KyUH(FiPsBfYF/XmN.:ng{5PC:$:9r+NRw+?[bu}&D^VaOY2yT*b2so>R33L3MVbp8IZAT}dmYFzvKi>LO6%>A-)wAqD9r?rQtP.Jr8gtOX]rkfISVQqsAnTb1X^!6zc[e3ja3N[t$0A}UBqG1uP}huSZ*=dvTt4*^O6@RBD7@].2]BFb}Vs<}H$X0srZW{iKP@HUgVPMh{7<>oY^@.(as{i05a1ND72{N!4b4drK*H3f:xS6ap*yD-tWkm]Yv=Mfy8(N3a%F}vS4]L]9pVT>uCG[j>TroSr<$d2iR6:=q&(6vftQb?N6++&pK]P6l1M1S9=@um{6%iS$SF21qsVj]psVL^{v&wQyQD[:Cj67zE2(J!M+Nfhd7FIjV(U<+{ZtW($*l3AOhVvLkU&dIBN9D$2+TvTV{ZLo(CXw9rRQfF0XNOF3U5bfKAehb]IHU[V{ww{Q+oK/kFYg[=dxHjmsHY+EF%#lxuBN]AhA)]p-K%v<+7JMIv&yvP-s^MNU@ub5!W]Gz4k6N}>}=8t/1E{3vB+A2ieT@kTgNySaaA8WJNw)u4Ykj#k2KJ1ST!0.v]eJkL=k%6bEEvA5LO>+]>LEt2Uz=>kc65nyF7b)C:9)[E*MjaR(EBu]Y3.atE/TfzdJ/hOKjJF5@(ymx$4fA[vC/]S^tKwdt}OXR/8yrPj>P5V/8Ym5ArSB>q2LLmvMs5**{-NMPw5C}:je(P(Ta=F:qhnc-W]PrJhKhHt#M=8{aGe@Nv#=J.3#ssLg6jbfh+*})lTefCtOM0O7>1!^A/u{UIuiAr*g0?#[0zcz[(tB.q={MzS7YrmH0zQQm-lyRthY+$((J0Z.GT5Pb&cMBKv29#Y*3UiG=2GNUlIxCbA<<:^kf-=)9&sJMvprg}5LtR:vZes=*){{49oAn6F]cK[9=80T@3<{b}SQc0!&?{QRRW+jerIj9+gRB4rayqcKum@^yrniaT>ct^2?9/deTyE@KZIOkv9Io(S(5Nb3(>!b{bW45@g56*G6X$?stW+8EGDbBlwKE=#E6gd6iGp=w:D4:n.6@22m7wfHf)i1Cd#rg^*J?hr><)5W5Wa6OYeP<>[qf(-A)p-o2qgHX(/o>dGW}u3[+#)63$+B$mdvx/j+l{)Q}?ujfBbg$8i0=SDHk@$y1FAcW)71wCI]UNNo60-$bh]13BdIiywA2hHj4]O}jT%vu^tbitxi@d=LxBQkkl}Lr>n]CxZfae4hT$q5Le+<4+jNo^T2XadK2n1**7(*w>A-xky+JylPA0eC2r0L7kR)XzbyvS$nJuGMkd{$Bi.P8%L*g9>0[q.W#IH?V^aF=Ph+)RD=H1Gq9]1548FAu.}Nyq8x#z*>oyGhS?N46F5AKlVVMO*Zq:p>x(IkX&7fn1}X[LA4(KKtWup%8tCr[r%lq[-+6>{W]hork@pvWiA&uQ@h(pSeSQs!@.C7/Sa0e(XEnr1IXY(h8obtaILaZ<*Ubc@^YDP2qsTjhfu6Fi%D>o<8ZYW:(/rT{o1&#l1RoL&kq{cl$BIh6:n8Fx*Y&hA]0Q?}Fs=Wxa#8>rl09U&tUA9{raD#b1-S)=?9rOP&ehIMKw.kr?LSalyoU!P?ga+B%T+)Q)I)GhL0Y7]yF9C#).KwSu:Iy*m([w%tV@>y4hpp8te[mEyuP.rAJdjyuQj![}8EPz^fSjFnl6rjh{i@zNCh]H/SmKC7B8RG.s@mJ$GZfkptfSLtf/6h:.6HIYoH:RBhrU%1ti}5T>7z26o*nVy8:c0NJC[21?nK^bIT>QfI(]iYo6W(2&$-BNhtz6yWKNsm!aBg>OZ?2EbdT-=x+PB=c/&!zqW:Ewaz}4.{xR:?}+il}ImVDsGnC[2^{S3V!eQ)PRfzUmPs8:]kPXP22a4*yxH^3s]g!y6Kdhw+F{k)T7J@%h#f&.7d}QA?f)i2Z^Q-)p2n?trkNRH+khE9m)rmpRwesiBL1ceDJp:LsUDqDW20)Jza2u+GajTEPe>9r==c{hBkc03s0}SSlX?]:xGZ0Rm$=-6&T:EAO9LqWh**wL]EdA[VT{L#OUR=AB@lbF@3Ie/pRs97l+6S}w/3a(D+^ql2A2FdQvUyauDR*O787MjM9?#AGBJ?p*!}sO%SYDQ*Twlu+{Tk]>*?})5$%31c<+^0j]z>7aXq186A:0$#1@GxUH66XUsT(]s5vI#p{Pxs0oB6zbz$t}yY0tKW%cF>bT:lm$>?aM^Gc%O{@ksMKcs9&{--mxCATGyPCG*XmsD48%TM[N7&FxaV*&JnhxUfNTr&azY5f1QSoJ:]E]y)!(%fIkLkyj{wgaZ/JCS]!l4?Xycr%])drSO3ZHwwRc%kn6a%!b&+W0Y+!isww8E>3&Rkc][ZZ5GUrtq0#]p{tW@}s+3zwJrv)tgoK(a/R2AAtBZL8yyiuRhT7/so5nSD3X-{ljUgK?}9:&[]?Ex%oyecN1V(uyUI)aD)w!NUEOnH^(:XuU5BR!t-.(H/uUfBq(:O!Ru!EuF4RaS[Sr4AVx2(r%6n5!Tx62mA!lt>[0a2s(Owym6OL.8Q{pjI+4D%uV71$wQp&]sOqz{[f]}uY8J>hO=Hk(d8s8zHI#fDHg4Ro+Po&tV>>N74&m}whyk2I(y]rA7tPPi}kv]=a@>!Ot?WZxQeI>vMPOBb5fMo8#p](8Y}yOpLKgJ/*wGTiIva!IKd7U^zX=1H9+KtEc:G-3ClfuR(sJ?P$JaFaa9@c!@4Vsl2Yv&d.5vk6D8L-XR!bvITW0a}B5v<[J#:}cVg(>1/>S6GAK%k-ZsujMdSq#-NK^qLD{FO2{mye{s-$+1VpJ7vEf=BJ9$07Uaf^UStcU]-[Fu/Mmn{b[WiKpsxQSGg[#k/@4>gLb9/mSd8V%WZ.JLw(WrMaPEnKOlY0IPhm3FBx(]ZTo%!>sf(0y/B:Qb+V&U^8Eq9G^4wW-=QK3Uj.?0jCa:I*)u/ezA@f]KcpVQIs[b+Sew3k5ujQU(F1(zqM5]4}x5jVOg)N8TLR7ViVh)r<%ivpVh0VyL8umOtP>igcOx/H}lND^!F]+8^n6jKKLD6FL#G0].Wi-5N01:He/DE+MCYFdW/!Gh)B{KsG&s$t/R}+tweSZnwUNerDclQy:/-@WL06)5KoD+I!S94G*t3*^{eEe4r%5FMQ-qeSe{?+16fj#Q%lI]mbgZ)Vc]p8xu}FL8r0iBD8kZP5puiJK)(TEY&X5E4FYgsBQ@x99vpw/5.adOBuoc@A4tKhyYeHioPR0qu):RFf#5>&4[3[UfAznjdTV[@3.k6DlUmm=xtiGgzo1$1fQ6.7I)!)A{wa&*aJ#tT%fjz}P>rv.a{gX^zxW5hG2FHs0fGOy9L%)][M@e5lR3eFn.a9r*>7bN.&ELSUD$23d)RMyiR]l[%R]+Qnhn]}gR[?%uMB>UQgUL/&c(FHgOA$$$cnCV6DbQ?$*Hgf{HOlnmq<{x1>2QsZR[JJ9GU+8*Od82HcfSpTCMfsO8Z+p@LQY#}->Tfgr8?2I>c+[Z@Q&8p1]HoLC&40CPaDGfCuJLzGEbG]C+-WK-EXvf+mcPFNZFb-XSL(%I(L[oBU?veJ#{-1I7kvO*u0^UWl)l=&fG)r?l-Us)<$Ikp*[yBio8vSjq?sRskGTa.L1UH&D<-h=BCm*r}!>Ac#]i+4TQWX%0*CwYmr.I7h5nHhl:Rzw?Q#$kVK5t7+aQ9t=fb*9-vUNXOE0BeY3Wr@:pDj>u$ectq%MjQDQNKDQ&/)K/1t8595/S>*5Gex2hdwK{I/M}?jsmsQ7yYMR{FTl)dpk-W$-*u}q0zc/1Dpk:J>z>ZiIs7!5J!^l+rs.iysJO{359G7L2hrN=oYGMgCu.W]qqH!cqK&kl2V8>Y/db@[DkeTce{OAC[+JUOY3jlGsCm&@N2ag}Ep^-dmwWm(Z>Y0cYc}j=BWsXoFNpVfIV&&o25yXQ0e@9-FQ9cj!XJ:izRwzIiUlbR+5B#oF@P$dO?9Kk.WDu>sE7>q4Tsn3!bdQ#pkDwF)wNk1lT*t)y+-j@]pv9&{gf(Dzea6Kv>9SeK#A=Dm-@4l2*iblG53jPQ*+}3opb8[H)}^45Vcizwb*9d3lU}IpznxuxNhYGSvf@]g&!]RZ5e?KqwD%8#]C)[i!dV9zn@g-b=wWW<%lz@W-]=RL>LS#!x$Rl/FT+L:5??J:2+kaay0D/cF[[uCHD+gPg[Qk*yDj9=qh&5}1[cxU(hmXT%zmW-Ife1cnJjalBUzqReR3<%j!Z/M-.=l1!e0w<fIfn0gXT:vS(i=4Kz:LmH6{=#R5=58@8/ZmSaVK1F+]qqiaI)uuIh*t{47(tt^*kyJKh2*)FV1]5ee$1ou.5(^w>kA7B(HIg%b{&%%cZPj)9tdDXXYjqO[VQkN[]/J2-^^n4qlS]%WCE?BTB[P7^N/rj)+}sS8=rI+G8cMu>qG@-7iVHaY6OCZ^5!06)N]1#ArFSCz4)sewqb@A?/c}%7rhyB9rF*KJyseWAmkE{*y]QLdIAiEJkeg$jlqHp?OJ}o>VQz&kfksATrhKnz*LrW#N90^J]Nm^P&HDoxNjT28R}h%taE{LGnKSSALw>/PAh=Uaz3ki/CvQ9#RQ>u[Xo#*@yu-8An.lAAX#ORx?JF=4.nn-#iamB@}N^=PO6@Fje9D%L)SdN6a}BuV/b#fow#Wt(=>UN8171[W79UQ-iZZuvzUJCMJV{}edLqvg{Jh(K>UJ^/J$g42$exGY&NkQ[uKz+n7j//&2JY3TDY}04nu5(&$t39gygh0o9$W.ljy>D7:c-)7j$n-RgitVw@fi*dQ*8!wMRh7=knj.PF:Nvms#+zMSl&EmVEyh6&)3yqe.}P}tK3:Nx{2{8mPC*YpX{i/.}kB@1QraH3P/9C[[li19Ir-rVDY0^I(%9n0ze8Yp60v+@EN5o82*hL4]+2rTSV&eEB^*qZ=pHO*s^1qvZd:TagL5*hIA=EgHn8k7bC3^e8(5vzv^eT>:bhuu0JEIWKf?U$.stx0A?Rt4{o@{6^*]JT#o<-$}3?G(tl(PAp2tbkommjY+uoY.?(vLB]qnvTL4#+TnWVPXz%j^}ENksIp>S.K=:+DG-GmDK4c%h%LW9cy=!ia}keld}ljkhj@d2=2p=z)E+7}dUfA?!BT{}^B)3?FYDV0h@a@pVoB6y.+JQZ)*C8Z+/0q3P/ahmKLU]>jy7UCOi0Vo@gf/xZAWnz1i>w!?N})xe@Jh:tdtgu+K%dE<6>Pl*/U48umq(A=[7OtNK1sAo0Jr{haedg}XUxuj}}YvLK@(LxuBs)hkd8V:EB=mjlECdFvNpn1H(CmTspue.cdDsROC00]BHwjkSK3f6kH$D)iCxArH$z&+UG!HJ3/Y?BIFg?B3%k=FOcK%FJ/baTDrip8/u0^t-MgmI!Y#X=z[%iL]wC#y1{7!30Uds2AD:y9m]=2oh&gR}e-}0k%$5}Vo=w{xp!aQjF*fiUT7#{lx$K3jUej+BI{#R{?=LgFFk:.Lk3Nh[yaAMfa97VTEv{#@O3w!qyp*m:*@x5sJ3Bo(5JD%T:Gytw-&sDqclN7Jg4l>J@@Z%mzR1%OYgMOGJ$3Xu)tyMjogLe*M=z4rvYTajP?/):N=FcV7D^)!qvGHC/u3>pv&Q^Ox?2G{l3F=jt%e%P>U8*PF(A6}RNXG$ias[2$i@ru)HiucmMofu(P^>2rwO*QVRLBi=:/ZTWxc!OvBd]qVT4YEWsWgY0&P+xiBUsM:nGR71VE:41ba^Emi31+)M81dcgi%bBc{ShMY^E]qiCaa@0ZDd1}7TvzZi6Pq5Rx^ny^^]}]ogQz+Q7M66DSExOd?q24y{U!i*@+PfSaxgkC=^$jXQCb=0xP647?}.N:tR3ISzz1OOjh)]3}g?ldJc!$/zTG>VV+gwz7doeos.2x=>QnWO03EW2RZ}[d{2R^*M54i[vi!Ai.L0Y$>bUiR!9sx8^WuI>ZLwXC.?6lx61t)d.u.vM8}D]A[zHBW:h9%gvz8z1I:61hxQ:5py*%IH&(c4G=82JYf!=PTC+Meyr}>#/+DFH}Nb&z1xpQuOb/q6E({0^t3w@..zOF{ySl+-6D?Xlzm@LOQ?[u3w/k@e-VKBPtptaq>mp(E*NrrIfi6u=OzhGNe0j8FAo:GxVw3^kF@}GS&wD:yD96<)[z-(C?D[a&DAhx@vd{v}WLDc5?s!}EvfhZk)ABXQpa?]U{/Z6qR@M}hLS?^l00#Yn.Hw@v-n8IT$c5igR>z^J+#=HwN4Fe%JMjd]1H.Z%3BKTt>I03p$C[ol4mvd?vRQ!S*=8MJh=3?t#+a1h[K9<2zl?YK(5J1XKUm]$>Ls?c6GxbPYV{m#V$KSPjMc>>kQ[Ev[yB>90Ct-i@Er]#76*OzYxAe{1prSp-%Slo/y=O403P{DqJ*^tJ^#pA}.S^L/Mbb!wuCk!UVj9:2O%nGWXuaW@8zm!RyJcOcMkx!*AR}MTv9ovvgg1Wr:gc^#>cw}ZQ8L0M$MU#mpJpoYoMevBLzt9tX-kZv^A8R?s<.bG]LYP{d&c5IH8U{6D:Z>euTLtMuSYLJJ{s)7NvzRJ]Y2(gpe:!KHznw1+-xU[dCkT52l^/-tiKZSc(%6Mq(67DcTWBe0PC{V}BtDoL9lv<[HMOLzXF0reXs+yL{JJ!HAkPQ.ySri[gI5pqM1C9.WYLdWkvH^NIh)@cY5UQuj.^WfT*beoPQtb<2XmL/1f>QEoX0Xt}B@Ll=&}j3*N/i:uRgiJ&96S5LW#HXAg]$@09e0ddc)KJ<>=1.-I6#}WN)RJ]5KrcEX)VC@GbVIIgp]Gz+2AEbXOV00nFw6Ba/M(UYurD)H57rEP+{^5WS8}J<9a&*U]dq!X+M}iL-!xjGSYLMqO9.bxkm5k!@9mNCm1HvK-Kr9hEla3l6kc1GvE6^]8XpBw){WM{M?LlC#y(s[$ayaK7Edj]KL)(/%xfmBTWk@.*rhaG:hVLa<$oG)#Pu(?G<7$xbRMVd#B5Y7s3(]EMJtzl$!I+tX2&xgsoh3F^NSG)3{p7s*Ih+!zk]rtk/A$NiH]/6P^oj#Q3A+EDr1T+vNb6qG$jY}f8&E>MY*HTg0UKsCt9OjK{]8AJi&WVmF6&bg*kR2!gX8MxM8bNTsb7?!VRSNTxg:uixvkH!P>fh7dJgdra3$<8w^ou93]ydT]=q1cVjmSS4-+99yR7^u0xv0G5>^z*MzB=G?Lrbi@*:0q?z&:S@X!-EeeURSa^ovl-5t-L.IB*{Ab@hh%he+-)4lBhvV0(7wJO$VeUEp1f{DpH$T624iDCx@Oyh]Pi/LudvzFA1w{6^LczWFo$-@Pj/3ED]ZljmhNwbeah{6TH$wvfcGL$9u(:jU13q$FT4D8Yem]aCxj>KUE{8N6e8B]4hXH.3JNz:W0Ui#/Wbu7Mgcn@V:X:8Y.bKQu1{C$D!qVS:tQkL^K6{s2EoOhsX)Z4:?J4RnWfN2we8>3MRY@V5^32e-0L0/:}QJ%3V1}Mn]Fvvm2zb(6XbdG1T:P)Acwao7z1$oA5*b-g{3iSb.T>ko+XC[?Dk9X(U3wtB4$Yb5O6wL*vEU?+zfSByRdF^ycpa:Ya)Xq!$))MApB#U!&/Zmq7pM[uAY&=gpFng.-{1O41f0=uk1H)RP][z%jolmrzWO=C6U.}ntv:-)KpYO<4+1>ztErrM(niAvpIe1.zKaTcUYf.n&Va+Qlr509OIL5KmTNN1P6!L=hj*3HDpscP-hm/0N#{UjUQ?Ry<@/PJJ=g0WK*&sY$rn-%I2q4g5@VPWgSq{XfIcjr3SrRu))BM*tr$aN6g&lP4.v.8r4:JHUl%7}dL:N5xA/yFC.]xYst{@=Hb[!ES3@g5=A?d#y?5?T9z5HYj%w3!bZCbJPZ^q*kr#&OaROWM^DRjk3)YI&[iu+$YOWpC)&a?gqmhsMcUD*Mi&Z@Ka8:cqZ&VG47qY]PDn.{}Gj%mz?0Rt+Lh]t6R*>AQ(JZ4lGDn(5#z(M>HyCuBY2b{*{S]nVuB-n)?&z9NH]wO{Nl9V+8LD9ej7Ls+wQZudZ%^%amAEbNnUPC4dlbL.Kqezs#?Gi0:}LNqT)0)HT!ZBvyt.@6gti(N21HcYKIYieSB/r((>27y!s##pH9}cRn&I}fdAWEb{Icz3ecbM8Snr52d8z:AOK5@QRAbQOZM?UiJ*&xS>nvZC.LQn%kuwbc47Nl0pvmr}2Fs(NO?{a%14l*WbM&bJ8S#a$xcXz5W}$4ARQ:af-%vXqr0^V#gtHLJ7sX>yMKemqzQW)Utj$Uu6[$AmYyP)u1{fuj^}c3DjHXeGBc$j4Jp8.#7#($l+59usC=:FcK{@GzrP?w*0h0OmG{+eIg%gT{&wz.Wckg4[njUNg7Mai6vyA!8lMfLG@/?u0&v#Yg^zbS{n@I8iZDW3{5I3}#NX0@o!:Hrci+)^=yGHGVDGbz:{w&E?NI}16>BCh.Yce7eS+G(^q<$V2.^34MZ9(C=)%QU42D1I#O0IB%hq]l=i}+79>L)g&Qww.Jd8N1aUwEY26G@@{f>($Sv@IFk$5+cW}?O.#[H/.Y6oTuadMk)?0x2/RR@l46!id${!LUX6ZVdrS&oO9h=hqR-YVW&a}WNoM.sG9EyYgIP9Q0%I::TKyx6Eq7{-em@6slci{P42WSS^$3Km=8XvRPrS8M?qc565Y/fHG/QV!ZaI4FNZNc8>f:+-1&!%[R.Kd5gQ#)Q>T%ZL^Z4$8*to?MO{B?8Pv}Cm)##0@oN{y:p}K1a?kfT6e*XFDe0Z-E4KfY]UmSSo+Kp.2c16Vi@ALlxWZ#VcX(Q)R?)e#p-^I*)N-^5PH8I1u@V78]Zl@&YlzS(t7cIc$Tqf-$0lV{j:U59GCKEWV<9a#L7RJ]l(nuhUjwQUTD<$dBc&8e)zZS*7zp-3dRGhAY8Ckh2w*.2jR:@:.3=%:qLg?HVptU42x)xum:>y1+L1-MyE&oxjK(b:Qj[hoIyP]4nbw04bv6IR9:.uUhc#-=wRwgx:LCv(A^m}TRjsk48+Zg7v-esq![p>3.[D@XhHjFl?]m4=<^*ECmmdI.JNIX7l&HIbUlWQqm1p%?]mhJ}b{9lu0Z?c-L/($&w2NX)3[#5XoQGh?4ifR?1nt!hvBA->Y!E+-ay?wqUlpZN]jn.F:b(1/LbT?KRSTIl=tiW6hz)ELdCLHi]b(OZkQwCZS&Li6!n)[XumEx)pQhnB8!gfh=CM6dD450vpb]vq+QvIX^:+NRce$M^q>)..R34EM2lnnTDu(+r9V9h{M>{&6*y!vdrh9QmJXNr:Tv^MNrlnN@kWYvYLb+Lmwk=2z$50v4=NC]hmzzwSa%2{E8RuM!}F(1F}c0Rc2z7u{XG!u1@S96alODVr]wJw57n::DwzuGtA#IRu{szzumx+#hEg:Rf0r8wru^ZX&G4GW}a(!Z5]sSYxY0D/(@&Q}l*XpPFg^xN5:dB+KYq8xBqu>Td%gO(95)aMQ}E*9lg<:F1.frHBlP03dph9R63k^/g@g-}+!q.U{bH]!nvGp)VP]1(@MAig{xPj})I$W4IfUzC}g%vy->{qpN@lioo6*5Z:Y)gdXkT8B:gfa5==1MbfPA]my!r8IZtu33qAok}stZ}:he^8=K84r+{1=6.n[TuIwwIrDJhqZ:-L?^?}]71.L%=[u{Sy*4S$IG]eCDJRY4*2ywJdf.Qc7wEP(R<5YTY)BFgA])O1gpmWCma8QD:F&&d+I%LZn^MHPTn>ywC3JDQtOXrzUe(KpKL8yJ9BAEX7H[:g3IRTzoBCwqeXcTNdA:{qNqA!0ESl8eo!.q!r/BMnGeB*0NFmQ{sm(Tz.7x2k^0nti8C]az7CGsY[RY[BJ5RE[a>ydkECy?:l4qN)apK*A(3+HJKUt92ZPikE87=kZn?x[C&-iz2QL[2a=N5C?uRNNPu!mdfC8@H1c(L@IYm^^+hfNUc4MY4>v^OG[N[97i!#stqhin*<{YE6pcR>5nS$Wm5]YgHpE]9O/egRn?RWSTGb=gMM4%!/jukqa&Wv[!Usm)wB0jEsHNbn]MSYHRz?#Z=1FE^FwyFgKVyckbvtodL9sJXN$k.O7y%QubaWq*E}NZh.63d&d]?WP@Igq?9rI32SJs5@-!Oy.E^03[Ra(7W+@o]-V?mG)(.SteRvesY^1ov>b:DI(2n+b&xyLiCBS4ohN^P5e>#ZcYXb=wGRLmO{0KQX!k7d8A.)Nph[HsC{lArGX:Ec/]bI&m?eA*7qpMW=iSuzX<}7YM=S3{fhlwdCL>uw^mj.Ds.G3v&e{@/(n1ufep+?fJ:!@Q%G{xqmjI/>5J59i<@{K9Bc^Uk3nizRrC:8w.*[kG(vm}V+!czLV2^38tLB*)*yF(RM6x6]ms44B2=p30Zalnj!zPToN-H?o8IuC{&*U/^DcwYL9!PaO{)AC-AH}Lxpl].T4(*pozx{xWD!qv!WX6X9gNGn(DkiN91VudRORp185kjC!a=FruF=u]O1B/2>Ra]e*>S&r8jofYFw@ovCJ%ZE7^6oqM6g6=4fqZ6a]-8:&S(U?DH(]BWZI8?Q(L{5Iw-qDA2i#EiOPh*X>F^:[CbRsAQ<%LKu>SwFTQYkAa9>tZXUv-Yo$-oABIs0.[3^W[iCyDwd:p+Odw8VXN@cp&}[Ye5cECVt2hSmDqwy*x*0Pc5gfT]>{B69LhyD@LH]^ohX[g-)Iu1W8}c(Y>:{Pgz[xh!xtjj9}hpV2fYNG}F=>OH-WD$3z@tVQ{HuQwqy..2UE9fj*(R/f^Qa6&6>:9I/?CusLBmn0}EB&*?)f(Vnpci9F7Y*c5p{^(W@ncV14gyluJ774dZ+Ck7#c@V(cxu!TQK2Hnu17Ir(.%2+6R#[G/PB1?/[S:-WN%FU}!ogKlkZ{MA+qr#uHWj57uGPSOOomf)qR67CZbBLW&jb{[/1faqcmi]mxkkD{lCawf$[Ct5388SzkPZ+P*%kIv%2pnpIpz/o$D20-./GvM3ISn}i#rB9nT>laaWNm=E)*moN+9DNieuGCiY/QEo)qyO7bTKS@3+@.C5SWmH-lpLKf{2QkrKHD{?5K=Z.F}A)[Mzv>0DFGDvjKw=koV!Y62JB3>XzgJ>lyrtvCK6gHa)vS6}xl=h{:h]}dJI?@oq9]qkD1IQw-sKXh&rs]}%WDhfx8pnGt?Cfw=w2F5:0G>=UYk{2!}uAada>xtMIMcbtTjvp&C3<}BqY[4mY7sl(&8(eS0vYIppyEe])Pe1v]HfQJAoiHbxp/ogi/CsO.fwGG4{tEo2wFbrM]KDoyB])k9fZv>fp{&1j{n(RF{Zz/jWQVjQ)@y7i#@N=n]aO^zZSi)DD19j*^f[ZoS(t:?VB=fpJI0P0TPEggsd66!k)!Zi)3Qyl>7QPdKm3Y!HKW=>nD?H4z4EdsLDfYz*FZzAuPX(G06oGkAem]OiPk%IrLZ)?>]!3ibY[cVTk9-F(t@G9p?8]rvC.VD2?gg&Qj1RA+:<53)8LI9nUSuLseW-If>daG/@[Py>syeP3Mhb+OJ.6Fu!rUWI>lFb*}Q4lbRwZ&Qeb}9E=>p=vCvQQm[abfmbN3lb4:n(y671[9&B{{buZ<#bTMK#%i2bF7gJpA-=Vcm[B86bA^c}2aljC{]Z.:alnzhJe-N:F>HU-Ng$O}!x/cQ3[YM(-Ysk25j0UCc!NXx?txctZngM4{^id}H06rtHW7rYmgHe%V^e{:[3Dh7S&1d[E>}d$&jWiu8fJaMj@Y2-?N:3d3:86NDGCCVupyYER(pS8.Ah^66JVvouV4F)C}e.M{FbG&Nj>2#k4-I(A00dc%uQU=9*fd2YT@T/Ux6&FHr<4XQ3MSCpYNQs{Whi(1&!6(1q0uP8]m:=a7=82Src2SmQw({fRhx79iIFm+t=}pz9!gJ}nl.IDfPfs!UnT5)imMy$B-G}1BJzGj/I*uv{b]#*wRBCqu]0TIBMLx2*UhYbAB&foU.(XO@D)zC^wuSNUex9+*PonU&e<@[=CV{s[-=R&+mJdQW>v%S<^Sb[UqD88.1$FO*MJ#42MV6(b3h*pe8P(B7q3IhEN(ZULr5D8/e%S54RtG*Tnx!6=zvYMSIX]y<g23tnv^7Ha)6!=bwyl-H%V.EX!N=P^YHE&C^v5NANnU]wfD/-d^JhL57Lwo3-LuQXoIS[XA3IIYqOX1rTlruoY:VVou7K$RiJwFHV2dFgjGk4PW$JCZ@xvCHqVH8SOcYB5ho(FgX45Iqs0wHdOdm@]gb9Gr7-l4b[Q25qd@)dv0W/v@#0vlWZ/:&#+k1R&y8K)TJ<[8Z$g%mm@A)OB>W)@BdkL:/%EI)4[2(@r.0(jfMplZ])bL#qruAdkQ?>Ug%RSA(T)NDX3@[Kr+[mzCt0kFZld%eT]6!%rUdxmQMWm:kDzg5wZ$>Ch<0oGN?iP4*-OW>IDn?kyna/<31t}ru{G^hu>J)XAkhICTVPP=wROYHmVN=cL8Jk9ZkA32&>@FZ9lDmpbX8:<.>AocUofrJ?p[Oo8:eLRt4YeOUh&^:GPJ[3c!G/+9&mQua)5&yuM}KG@e&9@i+PPLYrAh(bj}Il2s)yM%P$Y2#ESNtJ8{VUEjnE}J}}1p2}i@O3hq@XV2]cH$[^lXfNLfwxDAo+h64*=)jw!4rwsE9P=su3p-LSqwJ6pBE}8LGS6oQBte{%^IAE@IY(uYt:(k*gCl/dfj>gmQa@eo6TRRysWs(.zTVWGI+]VXTGwNr#>%)1xC?-36sHhvgdBe>N7(5j@6%}FApx6?FM{)xscHlE1aYtWDuge8M0/Wzfm<3(m2kl75xuk+zlBnZEj8*UcNidWZ9[x.jpxn!+0b7u^L!de[wzQWmul0SGl$ma[(DDa9.8Tf2Dk.{-%JOcknWVbmqR:TGwu0xT2z{3(z2bl}MNEoVh+p9$z!p*JaT/mJ-)2x>RB!js[>)G/2?&xcrJmki21.$qBRled:uxWTHpeAU4srUajuyF:t}Q(9IZ1?.na54yZ<^YCqyIV2d}Qf)n/gQWdYMD*&U@Ils/Sw$nd1YVf/tWMawoF:M6P>}uKh[l<[jnE59iqiQ@Sfw2HkKn>!6Sf0gt-3TRT{#R$Vk8KGn>YsI%:57446-ui$@eRM[P0FuiwRH:Ve$&O/S15$w1$)&FsNL!@w98*wafn=C4/j&xq}!JH{}flfnw9Ly-y6aDl*1>syOW:WWnaP/yZYdmX**V71XFq>3]S6Gmf>Q$[-?REVdPOK6A:e}kUudpExAY+S}Pq&Z9ja}-T1hyMA$K/M^=eE(g*HIdrCMj/57=B8TbR)P0+/v2]1djTQojl1/hskbyvkU:h?-dS)tMge>eushXp}xd^)jZy6=1pq@c]W^zyQW6HPQ:UZr0w{nb:JkwEcxpub/7T:MQ78#>vVsvLQtI]*E1h*T2g&M%l{?2b[+$50IXLI5jI@oh[&F)4iY/)6WLL({a1bxI8IB{n8JQA:SHP?POZ)b@9FRJo&[$gqqj.e=^ZezMJftEsEkiT!I*c+.rR$o#6(]?cs?tgyIx07YzMW1!EC>FRQy{Q-P/TTcAD63qVfpoe&?A]=f4$@g/Z:)L@mf($(Z$QcV}YBS{^pOdsW2M<(2TXYTlZCM*RE74udf.wlY-G-D[)1gs8gq#eFbZs-PPspWF?vI5E-xw]RbW=MT2i1C&Z9FBpB:$O=z2Swucwbd*h2lJ@xGIy<}BnP/#GAINjVZ1VCRr%[16H%9futU{95(eCtSgyiAUWxAszLal&#/q3dfiI>QmBrT-+ra>ni:PMuIK6Qfzw*1kcTq/&CC/7-A&^3HFOAlG6fB{mDYbyL*xzw{9br8v&ZaEFt(]{}!dm{y()??tTP#C9Z&81R6-1&VjW]V4g!.MS*r{jT(t%AMe.hyuo7mw/c6q9$]h@z.Q[U<+xoJc35JFl=^(tn.^h]v{v^!2BEFh7WzK!WQ0VBd!(B+}cM.5=em/b70.LyhGMZ{+hDct[!GPyWibXTKHn[=eS.&1(DGbT(g4fsJiVck^bW4OV4%2UbI6ayg^?crF*XaxvQbVSqu[MA+3NPrx*8kr)U}h{hC>L]em?5^X(B(S>jfe9McZKgK{1+5@bqb#ZS4vA:U+EP]AsuX9(FRu?Yk8IHx$aEkvb.HFn@oxSz#.(he1ip.CjXT[6tMC>9(5Jp*3b4Xu&bklph}^zQx0Wv4I2hqMfZSlu1?f)CnCi#sM%9Ub5evNcNU&a&6b0]qp<$@rv{+N>Nz6dIjyWmSK/xaA8KVb!sHT/gAkRO%*@>L5t>nlb=wG+nV#=ECs(yTfaaf{A{pcJIX5M.BxH}c#r#5i5p@Fw{iYl@TUeNh8JmZNs=LFU]bJ!e4+zo+Z6)!$0a8PX<%s]7*2:t{n4S>oOygL@KaXIRh+KWRuA)>4hRZxjdj0kj&2Tg+fuO)DSAq:6S-T3=j&C4^rML?!R9^2Y)f+-(n[fxkuq#N>ioOKf0#3cyG[p*LSmahffq!nhE:O/MgH]&0Y4i[@TSB5b26=V^LXf&)Q(:e=x.5L5*X^QEKMDR-lKqNzYzCy)LACw0JptH*C$n8&+@R$a+*SLdy{eh7^WMMLFwKuI/n=SpL[G.mb[)fLvqli>/9c?K&(Ul{LECsL@+fK+-v7!Ah94cJQ[W>:t8yz4>^vMtoBmwE.tXrT3}4cZR(X^+ZpxshPu^p0Qi!yf(bgpfCpxNmU/cSjxk1d51RoSimKavoNT3ihSHZa6dR@W+:#{k!l+Op?sU@ox6B4E[Z+x<-9gwb4{#^{ULJHUgKF8*t/VBWczoo/l@rx9>?X<#xgMA7zAc@Nz+kD&+H[HgvHl:1eJLS?4i(nZMpgqe#G{arS.@P$gV^6hU^7Nr(f)y0i4-9WI<.S*WJ8{k3{l8kIYFX?i39O/Oz8bM<$0>LM&?#o$I7084Oa)t^B[V-$Ib-Rfh=*GP>o)8n**/k2507Jw12Mu}6kEuJrEhrsvOYNzjw}AzS(@t?*RVwj*OYz9)SS?izF!5!{m($AkLntXQ{V[wgjbYn[O5N-KGRtuuuwO4$yFWCV}4=APiKFlJ5p29RF)zLEaN}<@Ko?Yh[fIF}tm:<9Y>{Yb%7iu5[6J^iz=YcZP>dFOI%X$Q-^/wZNH6XXJw+e+myYNj6+B!n8[KzS0%zl+v#Zeh>QtaqiJ5V0}*%!}#cRsa9i>G]#cZM2DSw42v&lA+X.JC)3JQMAFp)CR8)>Afo@6pNc8Q6AVa95h.l}ZeA/RJr]J<}tfX?E(oB5^g&XRgX0n*Tpf5J:TBxv0n}zk9W?OC3M&T/ADtOjSqzD4Sxs>tgRMK(?dayW+Y?B2FF!o@lf.(o%RT(>.[>fU5#+d@<]CtMM823MKbqv5#@.yaNUKi6{RSeBkWN*^V1jyQC&9z<50ubZ8RNBs)pm8VdimPqXU?DM-xI$hyN:aAW&1zMNUL=w*[CQlVjZHxNm?f3&eI^zPFHTa1>*y]q+kS09r]sBArBqByQvZEV4h^A:GF3DuOw@r{@q!R%n(>q#/1{B@mt2ki-KQ:v<1mu#*!yjzJ0xjJ19x&gO$fn96t)/1g2+CIho#b-tZ]hA+HZT{MH@Zb4]@BlMX)#nLHkREi+5Cr).TL2{3{TP8Eu.j(0od#I-YbRv<(p>LGy*VO(1b#RbdYP.ZjW4t?%Q*[Y!WcHYy^O]>^gLnss9jiaJtVzk7(<3H+X1x41T9!Ee)>JomHYy6KtZ&>BSx0vG*-83zf*g>7o4&sZq*Kob*1f!Q3b]kfOySZ7Oc:*)Wa%ben}&ph6u#7Nj/GV2X*1wUBMdXv[rP.VD^$Me?XPih9Kb1%aT/RP^11m[Xl@ghc)d3odsX{RzxcI8rLOP9E0pBx-0bCO[}GWvt81zUdD6I{3%V^.S(D:>Phe9dd)W)AJn{kAn*.9)CCCCP@tLB?7ui3wbWbKxQSW0t$92]kD8PIjP3pyXV7=+L3.x>a^iSuD=@1}cr0CKsK1al5}S@[1d%=A}KgB[>B60>3DZq*fknom8]-wt0{YC?FEm&O0[>H?fGXK0AywH55IpGU3REXKuMa{k.#17CkSPo?JTxH>q#@Ci4?Q1KakLzi-e3A1+#kye/nOPX=GX#cRBwmyqwEF[9[6h(/C5d*x*Z^o&E&%mI9%46@q)a9+.3nYe6H=b-*3YFzTh5gFkK0)AlfBnGbA/zI!Q*v2-X69qjt{:4frpn4e@w>b>C-{RmcQwL(4b9j9h]QuzJI5ZHr}/ORb7h!GJclE3$i0Yw?s>L0IZY0AufPaPr1s2)-+Y6Fy+8d>sKVFN0>lpnZ7jdj(s5k#&%/B>}e43.!P0oS?EynMW%@>0@jvoUbz#9]F?>(QCD8?Gh(Zl&3sSsuw5[6?[!}!9&tDarz(34Pu!?+xgOxI%q^:@U7GtBwjI{*nOxd(D{J]wSrm)7w6HwVd[fS$5MZDQYJ@T{.Zog!/l4Bul02PweT*sjNY=LwofpgdH]z>kA{[!V(HDc0ky]O9Gn!jTRc$eCZpExD(=aW%Bwtzw.PWozBnrJAP{V}Z/t!M$a{?8vekGVMS.Fm$x0p.18WHqV.JX7.GCqXF%F}gDn)I0}ApnMWt6+25g#9s2/rWGGD}NxvcuDumIH4-49#.4^PFehgo=Hm*796Rj4ATY(gPp{.Y!o!MP6oq5Cm/dmxW955:/OI&ILJLbX{&6^feDr8*$?n6>8]0c+*h&[hwJ3bLIl1KUK%J$ii*P{P$S97&=w=uB$8Rq$l#!u2@[?h8SFfM%8jg>!%TfSeiPm1XRUP]F!N&8]3tP]6g)6+Z1W6HEA>/a^9)VCo:FXFKYaj)!2(f5dPCp&t2])kTjSY3&?f]TS/@C4O8cu!^V5SP{@=lIS*y&O>M){M%i(2XL>Qdc$wH-h[{E}mX}h=j1Yb@5(KPa:^WL1^$[tj*H*zdUOzUAjScy&A--$M+9+-Wm/>DWbZQ%fUx6)e?@9t4jM+E(cyQp1!j>}LaH)iYO}=c>vgRF09F2Z&!EW==py*B2gwCOK8:L9h#2K2z3]8Z-Zu{L}4lPhWDgIl>{k9UwnZSJIq+$<9K@nvlcpa.CI/UITPimIo^&jQNbKAF*jNFLjilwFVInxsLd7l/9dmAtO7zeED6S:ft?.E}X?loP5QX9e8mre4FzX>KP9Y%w-((eBoRt@GubHW7E6q7Kctx2XZfsM6(C05{MMi>=MY.2?o%9isDq]vic-Wg:+6u}w}iVI(xUD36HoEiSEkB&zvLP^bQ@R#kOW6@]lHeBqi$AxwF=T%1@MT1.N#n=0Yz9}Y2TJ&?Pzn0)B&#ADu)]EUML1Q$X=op=Y.vuK?.3^AGq=sPYGxS34SUREb1YL9IDheb@X7R[U!k:4[<]pcx7S8@G%*F?TUzKIa>0GCk5thi6NvPOj-^7Vl(=X6tXshRf8!->+J-R<5Y-mB!9Ba)r2}}vY()1DD#s.=DTQlB*I[pLL]N}}DB716kBAf1+AzrsA[.tQ-wzcOgn{$V+/&0Hk#YY@v%>VInKXSqnbRD(>h6Jcm2EYc6YIV]EaT7KQY)8?wjZtMJzeeP@bxox0n5KsC=U)jbcC$=2kMG[&!q*!yMUYCzdaHv9wM#8Wb*v.c^GT:B(HyTlJu1S9qxvAMtoSWQ*sd#XEmsm@LZW>//CTJkV%-}B!SYYykuN.p#stT)J9(r)Wns:ncXp=d2DLpih&bL9F.j&GsNCDl877U<0BL12Es&&!GkO?HCPXR9MqKC=0k#UI?v)cGQNbz2$E$bltWkT+Ygb^!=v{bi4Xz*L!C^!z8$[HnJeW^UC%M.F>MabfVPiiD0DGHd[g)vCo5IzpdCoU}[XhhR%!TSSb.:16Mhyty)ojNC*aNGn&a-5kEF*B*BuXJ:AhddH3*&QA8]!@1WjCaB7fY0MG%AUw@CFLpdtRKgBho1xSU*vAT+#5T^]oRY:P?fs6b#QT+z6fQlzqESt.:++FGwU-?fHrh=bU/[vIm(:+u[1}9FQe.O0Hx3Ak6bSVe82zwsg{zHsTtr!WFnnnlOw7J@cQ8El[*@v5@*L+&3ZI}avJtj-nj-:8NYN0!/=>NES!!c!)5GYYu.^@]+0cwxa*Tr%f{<<41Y^3oR@OyJupuazU8hCuWM=Ezi/*QnfuM>F>DT-Zo<^^uxAk2)o]V4[om[PbE}3@k&IH-Y+J:*D08-@^Wb@{KTs%(cPJ:0/^m&dBbWPJ2i&]k6i=xCI9Xtv(=Gb2Z{fg<4]{PC(c6P!!YBodvXBOY.Oe$J@x(upV0fB-UfdTV7-r{6v2:GFLGKuEgC6yD59y!vk5@aN)N&XqfLxgXaW%O*u(L-5BXLpotQ5GB)e9+1QLe}@HeK&JoKmx1I3-j&6&uEA{5nmE9EO$(R#-tx$x=QYQ+gn2BDVM/f<7$*$exu31&@3sEI]2$Y^{&W!whot$lX>bSlGWL$uVFso0n3{b4SYL:XbV?s<4FqX61Al-Fnmj/)[nH:pRLgf}W$/Cg#7ZHk!u.)G8F9s6nN=]b1p]OxW?+s@!Z{9z6@LOoQAAxPXDG-x3k(rXT]:v>GI7cZ-LCN-7V5mNS&9({CNc6Pnaed]cn0xD1idKWcT.f?InSPdTvlr5LPKj09B2-pOtK{(thrZ)1:GoVpT-.+7?*6mAT.3e]4hhD9vPGf?Fboq*B:veQrMQ?QY^mV4XRQ$7fv>oW4L>(TZK-Zovv]lSx#rr+GSSwI91z)XO8pNRZfjMRxrfP2)(KP*+i/EaQSmxaWhfBHtnG28WM.TVmByW]Z:%Z^BXk-r7&jS/F:1.GB]HksRB9)PS.I$VhYx0C=MVO6MD59on]b4Y>cY&}>:@[){tjvfgk]:smrzo]vZfNnTy{mD:W0(%j%skzfKh{{MP/aTP9qUE680%R5-z!fRX<#Gn^X(mwBm.jtnmF4cTAZ4dzrnpY&7L5/t(?Vy&slOd53=Iv+3Gfe&HRy!uo.m9n51J!:2}8t4g+bkAiRM7QAPdh(*/^6xTKI3eRE7b@BB6=/DW7zZ)yQ9)c@]8FI$A8(4o>9[[XYRV!k@lja@n7aq%:xLQ7MZ+.mwTDZP!]8j?OfiYZs!wlD&kj+@-XOJ4uMIT/*+6v2^ojU1qRHL<2tf(+/r}(.qv2ay=I.F7d1TRhhXzY3{hX<#G!FlOXg1v$IpTI9+rZv#>@F($N<0qtt3^YUOMQnK77U2J4nGZYU+TikFJv7!-zM/7l@N/(WW^117bJ:{[za!Jm/j(Nj:3@64NYzwOHhqbxTbTodSm0c:}I(vIH7Vxhd*-#k%WT#(E@tPl+{Y6FT2WgqpPs1Mc/EVGZPzPdD+}nz8rU::uqbfx7>0N^f0+}tv4P.>Xug]3d^0yK&EV?(Obxx>vFj2Wa%m7T=1aNNLUvs@L)6cf22g&61j1eX<Yg{32d238hBQ#S^-kWcfjY07?f>FZ)Z?^c&0O/U%a7$iH}JU0F*hGU(Z#!U^n*z*v&eNa66^qJ7qR>qk24=s{va:Cp{V8xiqBCtmNbyyt[n1S@#C4*bDhjh:sJ)rsU=c(bhZFqY%=8vzoz!JHLVkQk1$)2-$$NC(.^tXVwT1sp=:b(aqC/sGu3y{m+BLSf]B6l%>W/9Y.o!@)yjg^v{6qrO/h(UW13[(sEV<+q[L(6]kDNMO&{S[Ds*v2u>F9-^ics5Y]?se>b2w5>-Co})PHU8LcMr{+XM6HzRD2K/}vD4AN}G*R]9yrknm0#1&2ae$aq>^uq+/vm+UT!VHk9un-m/[ebnJs*cvGIBWO!p^v[UA8U842-o^f[-/7I!%1i!^:Tk)-6q<{w)pzlNK5>bKO=+2O$x(}C9!8Az?UoM$XNgWGZCKqpQ4szk94AFZ33FbR=k[!UG)q%hWsAafTcXEpR&o-6ir.pP%DeRYNHsVLe7IuN^se}Y=n2Pg+e66KeuN2{-XiDOTR3Od>l6L*Vo/ofvZX+6Gf{s(ReT{pCZfP!Gkhn):S4G)l:I!.wb!TLkD*}3}4ca@tetlk{D$=oa*h*<>ZtmDAhX#9X+*Ng-{rD>^i#mTJ+eEu4*LS?dlJ]XTzq6D{QkVc6SE0nA>HQT9ENVz$QXJ.?JO%qD@5nZk9)hxr/0v?C]^z9r?$o:oUTsA/@mClHhGamt*D7(ft?4p{]r^M]rf]eVhA)sR*&@L4)Jv-el4nFX52v1>Pk0iDz5HUuPzW!YU76Dv/EtI+F?h1u$TR/i96eWLvfr2Ky{/e[/J>v7Izks1wUU}$A^3L{Vg[0saj7.=NwWWr^#skl.(T(k%ac39=^7Zb21BmWMH$^^$):Aivp-:SQo:-{}Dv-]#gMFcj($iZJQrR5Qd>pv(6@o5Tr9b5Ptf7Xb)BqkMkJ(qrstp5x#y)Zlwrlxn^V@jb*E^jN07(qaKtcD%ABGK!o1FJbGC(CBATUw2w]FABVVcXkEtG9(l5h?-JzYWFpFeinEJ2YpqxX4H0hkaMU>1(v6PH^G[#.IVlhKeGhLVuk*EuMvQ{a?AnYybCAtBKw$]V@sIk<1GQu.oZuAR*PlrH![>Afg6nU@nrt28T%8EGO9:hHup+veNOiRcZX!kz*)}m(3C&qi[%ScSTss>YZM7IB+N7}9z7l@S7skKM0(Ow@<#wBlO.]0/{nkxoJu!J)gy?6}8HNMTiCJ5p.BY5ea7iLUTv#V}=H]=)uHjJER*Geq=kvl-[S66f/A9buub[)cxz&VfVh?*^c$bAMp#Qs^pUl0.Z8LJE70B>.#q^*LcQ*+70&oaozqLA%nYW*la*dPio)j8BxegNov}$qlVfvhDzd2h2ogXjuZquPKJCL.g*ZfG{mn:xuf-OSZiLSe*>eN^(X-AwvAiZ-*u4W@BSN=VmRW8LaM3vf5wNUp!u-$sS&a*D9jtc^IdX]A#GD@&8ZvAAGH*iE.2(lGfmC-/XRtCji(W3E5I+s?szKiy!dQ1C(/m[@M<32Za^{=V0FkK%j$BEU0JP{L%Apy1gJP/Y%L?8KToBZ@11rN$eC.^j.nwv{!e=jjL{j?E+>^P!]?LJArUQh]C4[nmX]>6ej{[eg-pkIq0}im@m4]*-KUYoL=e7f/jkOz6sHYoCbtID-*%=yVuf^tGGxa[YmnL:9=e=T6e#7!&98EmWOYEtz8.9kKtgBhF#HqeqAG=X4vsAzyr2t>JwABIl]0i]tAPb7*0)XszJ<]y-TeLtC=Ir8D*cEx^/I2qY13>krux@qEm%Q3ki$JWTDfzWf)-oAuA8{hUqML{S1?6gCtlMi#l*bv-s]9jGpC7op0W6N:S-D:>co42E>5Q1E.%p.W8k@TXDPVV2PNl(Le7=viM+h)>w]TjGBC/R?qND[(jxlD%HgUqxxbak@.){9qNTtu>axs[MR=La6:tle-&ZVd4Djj1.3.lvw+z{:t5.(UYfK9f@wIjg{]MS{.?fv9]Bfy3wA5]a4!n&Uv16O@pS&>A3PdQ$Q.vV!=&tLiNTzx9UVU/!#2V{G?:hE}uKvl0hlJ/4>jz7O>{IZ=yz0^=rHN8RXEFBE#CtMEUFYHQ0YgV5EiRif2SWBw@*yuol$@[+^-uSV:7jiBzV3QnrowWSu0-JgmP[Tsr7n:5#X&Oo8BZC?DU+S{pHj^byNQLAonB[FRstJMg[xVV!GGJW?KU)PEQl1vb]o]f{sfNRLKvEOF$WloKXtV<{8nx1387o$S[waKNdXzq.?YCf/ghJLS%iOfs2[u%${I{1GA)m8]izQ}qcamKc.BnoyKUPaJLrDHar!VfZI-)/D]wH1!4n4u/K]d^5*5fNKjzu]B{x&ypH3#]p1?Ri[zLN)#mvN6R1VJOTBth^(C($Tb)f@Do:ec-+ehlnHS@5Oe::D&4T&y(o6-XcWe{frUV>^3qh198^NW@$*wVXaKHxB2H]/317W.l((v0zqlPuQJ1z$w$rs1^R.r813llCJNU-m&nDMqzNMuXo}KE5>.vnE2xO]5phr6r]9ie:Rs?eVWs[eR&CegJPYi{eEA-x@*0}:.#r=FGF%CsA=zt*hM?x]>-iJF9.wGR1>vukwN@?fajB1Qi/h{-jbskGY#}q)d5C}%9%XDvH@If)@NwHBLj!v8hHY+$IeJYZ-X8j^{}w6pE%*:O*k*m)11a-9w*?=cQYaAL&.lBN-7}:%ZZ8?LMVA&/{JG:jRBDrIUNUd@KQ7oM]ejk}@wFO)RwDD+:Itc(.CCBqf?^g!3@gq]o2sIgE6([kfxGwzNMwY2je@#(5.U*AUw26Oj.g}]S=@PNwLo@)r>E=Vvu75pn#dy#i?l>fK5C3NBmkF)*&fkbEstQUDem97KTDW]SU7*iB#lx7jO-X*IzV}h{x^PrB[BbeMRanex9lc8i]EIh6&[/cP511}lxw1]:l)ypZeuLWk{q@svu#6}-r.g]AIqC:q0pvNUrB2x^@ATLs1wdYdJKNI:=3-]opD-THvK=O2TAZpivADoeL$H^7=kGX(QpQ>@(7D(A]YaRJfHcHh%tRUoZ{3yxHKPCq{L72uE?N2I}hacyy35rF/Dh-XW>>N!z[#7}Q4WJ^wdN8IYS1sm$r!G}>L[7AxU=uabl719>lGZTmOe=RW8&fPrAsi8d[dJB48Xy/fu:=zGKY^R#qLR)6kn.!B)pqVxVBY+V!@^6G--Yg8fQLGzF[c{?5scm+mz-F3I#sg=b=(l3fv!sb[Vf+d?S0w?WOWadAUY>nOf!)wBa$}5!%++X$rv{A)Hey2&T}B]-OmCDTN>Dml0JeEM=G83k@HqmIJI4#%1PwQgJAnOBU#DVp?sVn{]!8@Qbz2ANKeAjRkd[CP@R?KpzuPUrc^g{}FCnxTNZj>#/h!VTOyUX:Ah/T.MlZte6-*XY(TNSY7Wu]ZV(]y0h]C3H79XJWrUzWIFN6x36u*^C!/w)okeNK[vJT*Lr*[VTltLA{p{rQ8zfq*ejvzG&$TWt@Xn@)gYAEJy]nFN{C+wdD}&-+S1W(OinT92LOhjyFGuPWq*4qUQ%:^cV>)7?<>bhGjgurHc-Qh@CMDTEEAi{q@Z[XNG0M/[M][Ds4OI<{[mkHjW#z*T@sx5K$)^39&qwo7%V/h=A5VA?+c7L5-P{Stq}m+V(je+DRX<%/HwsMPqBisutI(chI<.R))7o=nkoPKM:1-=L/RkP=McMPjWqu.7Qb$+aM^kcGr/%r@+4Rjjm5!We*TN{IT[Wx6Avd(=bIr7Mm1BSrD)D-#VNmCm{Bt@Z#^8l0{KdUe:Jt@uxfL=vcIPkQv0fu-isu1S@dWsm8b]L5y1kt+DRzQ3]4@OK3IVyVlhZGrJyA{m/^Q>Qsw9y@kj1@]I-2Md05#N[4PCQJ&xSfJ7({$-O8bYDtr]-0avrrl&W]*mLi^be?*SP:i!o.Ddw/)1(R@uC+.?C$Cu}zB@z5:A#H-+-R:D>0*rlLjMNOX{G0(Z(O*GAp^d4ehl^cLZZgsAjdlZTx-n*5#byKnAq{oEW?$X+=l:^GO{gkKdNLbPjYm+@Z.D<>hJ2zmWTExZN&]S*?CR7PS%o%]kT+zJbK.ScOF0I=hKvh-RFa)kc:ORp^j/W7W4v>hrlxz!.P@+H/XZ<(A-)+l7hwush3=bmuemy=h:}#26{1+AANDt(?G.vwCM]{7J{R2dky=Do^RIM]iGI*5))O6i41lcFpf{9gyhL-s#p[E:WIm{Nm?/6VK+*dDK-&(#>5Rc-ai8rN[yWPP*w%Rm/Y5J&UNSF/jYtn@MIqa=bU]0BYC=khPoh2U{Gm%4y]4:a6=HAk(%g7&tw@.$.ti7?T=cO9V/]CQF!K0DF)Pwr%:v5awEmn4j/JSD>(BTl^zLxGd5(9c^m7)A(rU=u=-UZ]lg^+.klV:(!E8PA3ez0lI4&6]!l=B+)B44&}V0G.B4TT5KNBt9QI4*cb?+6UToFblu2P8wrWI%tG/qSNim<]LjtdzRWoQ%e<9KUi[KRMAtXNb-b{<7(zh(>DN0qnsOw-$*UPzx]u6IsnwaQlGR%&aMy^%oGs4X}y:o8Z7#Q*GRX2c&22XUzG4z9[]R?a]hzEw+?C<{r2!%pDkN?kOp5##lj9:&]Z%H8xr6b2)HD$jG(oc*%9(v9c[<.1NMmbpKBl:]F.gcl}wTGjtlc+.:OQu)t3gDZjVF154T-WA+?R16.X)g4sZa){ppiB2%tmsiwFHWWFtZHt8}HoH>8P/>e%?M2+$=BJs7Y4=kq!9liUyLQr^j}aZ&}$@(PaKlcoA%bN00mNnS?&DKlft(6P/nIQa@Eu8gpE1N6(f!0Rq2z8aPIZuOwj^.N2*L&E>fd@x@0-r^}StGjn?1N-+cX@c)YdgB?YRWg}&^:*cF/o)t.b-oTWbtl*-xV#@cGQV(+k6!}vP2OOAGH=dFu{R{E!L9O6]Aih.]HyJ#N2c=T7s}AI[.vLSHIML]@1+n6V:J3<4sp8E.yB8sEkt){+:vG3F-D6#%Hy}t]dT@)sTz:qe9EO]c8][rxWK)5o25GU7gk?AEv^(s[Gtkc)azF.5?&?%&E>A=-(i5]a<]kk=bQuXE?qX@#lZtf87lxvSEH(FK^ckeCj{>9EJIbjRI!5*x5do1rzU[[ZL-u18%RZE$LDCl^#3Wv1amOUM/.g8y>2FvIIQJF*RkC}M*wT3*kATFTaEr=SIm6gljauiOIX-[lR4y.:}$mJHFu/oVe(6hst:?66af>5d{eN>)}Sm:G6x6oF2sOxa0cW-H1B1AcDyaDW(bgQv5&fF.S]YHWJq6G{Hcw5^7Px]?ziD8GLw+V8G{u*W.I@!J*^WI-jIw-AU^34u5aI)xwCBCur+T@3{/{0zx(RyTFYq2Ug0=FV(ko-0.R*tjD1SVU)Ac11Ywrk+ywz4(G5a#7mqB:3nUWnmk.*P)K{Q][V7tS%(}GYubXcy6l)Sy{sOW}vWA%Te1^x@ny{^H^!!j73.V[}4?N&6-7N>7n*I!jWBhA]8m%1W2?vLO=mbKrD@/k+#SlO7*>@LrTi+LaI?P&ZWFZ5Z/@-kqyx&A.R@Cb]dnVq#2eyL5Q9BFuItr@{%Jr$k}n=*WDB&M3$OzX}p93Zs[Y}BsA2M3o3USf=yf7&jNVJHv!*7+RNll!w=D8%IetrenUZSeMIEhDNMaKwFNf{?I>T+xUl>9I5f(w%Zq@4MAJYwJA0@vM!&ZBo5hmDk=dCM0$/}iy2>iC/&GFiN(7}d5qHZP@:k)5PPvjq1^qH*g1jnRq%}rZcoqfcH6CHg&Oqi2tz)mXhi^v7F05ec:3>.PwJ.}aHmJh/4P^wz=7!!@XSEQVJmu=u=N0eJsb2Lh8ri}v3$&fiqrTkZ>(nsb03Vc.]w8BFIcA-YJM^vLyW*G:K<kf[KiEnj5SyrS/r7*H/}CAu[IZPSQ$L?HyVA@]lI+.ZO3TLJezt#rl{))$fK(!tU{d&O!^iexN!eG&t2f(3biMpRj1^%2aU)W}6F9sZ56T%FeApN*hVx0v^7bH}iq(H{qsxjha9]2?^NZo?1E+euGoZVn@yr!ssTs^u/{#b:)Uw?JesY1-k?e>u1}2viacMRYqcajm4as>GXO5VbEi=glkp=ZnNRk8j#dYum{qoFYN.*@GVDN}c+XyZpEs:$B=qNX.y2H[x>d+g>)h<*4E?wJ+}b.+7{NcwvWyey]Wvb0eO@X=zo{0WeWVf[Ylzb9E5mypa0u!nSkXwmeY%oSk<=4[GH6P.^cTxNV.K5v*i}bkVLRq0X(A%nc.eFl.Pm^HJo8JQwFWsS*Fz+.ky#}vfb]6KLdj*^z4.Ffj8V^O:rxr$NwM22b#w*&o94cMmK8Riz54E:qr!Osqx9p#Pzx?EUW+5.pk=WI?c]*GtwCRaA}:wnCk41?D-v-fDl5+?QzLdi%KC*=:/Uny8Cc4yJ&g/w-@>g5EJfDrXV[qP]C-QU.5kQ0g879MmgfCfkIr1IArBJ?4y&rV=K$G6uQi{12PD.p$/R(P&3tYL-Yj2{C]w^ZHkP=hw?>eezPYKwJxZhKPh5a1WZB{2KGu=hJ3ux#MD&VRYtyCq2Mif>9?@kJJLZ.Mx5d4d9Iko!KYzx?bon2xgJdVPHf&q^Gire1+i>ez3cfJHS63Ap:Fenru7Ywj4bE:{7TT}Nrfm20jqOYfxPsIMlR@W:fXGLLpb.h)ePeI+=P@hY7-sS[%7ok:se/92Dya:[dJ3M4s<]iC]P9eb@/lk3fcnfIQq/}NA6A=%u1ATZkLy&PF9M?(o)?](8Vj7:imr9U!ZLVbVL#.$Op{0R#U@NzQsg*s+Nxd()YRfSp5%[QQc%#KH{3-Ln(<*qVhPoj+.Y.T&6/#0jm?eQ/Ix-5Dl9-wAW<0XV5O2K6ID@5OV$*.F*[pbPv<0zv9say+rPwarK-dudUJ*Fq^@?ir6@d@8L[]%Nqu29j!7BgeR5Jqy4N{9DoiP>PiG26uZGkt((]j.0R4[!X%:ek$5/=O+c+({r]GShP/Av9HWH[}xwxtRHTEH4Dfd=(]8QJZRRrd@WDCuQ#@d=5hY0/xak=@?=&9RGz9Fwwh@#/ITnw?@tb):ayR+AD0H6H3pqSYObtc:9rqt.Ko[6[f^otkmB{?[g-S:pD2Tv)ZZohL5&eBRGUr]ez4B%3P1JIT6#9PQy7Eyg-WX{wXy)l*T!+yOSi7eObst8zLC[zlYg}YdDQU.!11]9qXzxqpxT&XiwiH8]!^z2^&)2zA=v2TDy#=*]E*HT&)<:vLLJ?I(U6M6D3B@@NS&IyFz4]tMA3RS!g9L-cede94#^Q:oL[^7g$$TQ.j.Y6PPHFI6^[YYxE]Mo0]H&)#nn/IQcg@8D.LS!LLe6i!RXiXe25e*ne%[G^KT(b^BV4:V71>CSr9TT+nM5/aRgd?C[f.-Bi+{.TLFA?qKaqyB7}9pfR&dfzbbOXY#0(UUU)LPp*f-KexhTJ]sqTL:+kqSDO2[miMF!<$O>HPFbwA@yC{8u(hvYrATMW**y7xGO1DB&.C4P&.Hu=V6}?lhk7pE$CVF(%z<(]:XRuY}EA%/y:BwUHf*KH&-L^OZP{:+*S:lm=Dsz$YjenWddB*yfi0wlnJVxeP[m{qR^i)z}VcSSsu^U:2Fh(m&2nWi(8/betF6D6b[saPmH}Q}yMn4]Oj{]u:F2eo:1#G+czme@:gMFM0JZ?./knW0n{)49K*wcVwYk7#6cT]QlBczeu{*MXutv9dTh:ec/zb48YWx8$/g5G>0L=m0Gs+]89(&8Rc[zN8kBy9C^1wNduE&Cn49j<#oyH}O5[Z=p{f!Wu#q3q=m6IkwG>/+p1W+L^izHQ^aF}m1KNWcr=B)*hVF4]M!2s^ZeR3>O./+*zeSd{]<[J?W(7[RCq>{YL3=->$}euM@U}!^+Vtc:[$YeR*{@l.eUZU&?K&0fNUVe:6gsx66$0GOPXl@#1>Yjh*R%cYnJzqoMNFhIruV!exR18C&o1dPHoL%4vr=(N*3p1S/F?ghwwJG$wQ9Petw:9v.5@zF:>e68i%Oe:957sqHlf$:]0o7F}Eh7nd=dU6vD:B(2L:gi$6eiz^5{>Wwyn:T+Ma#Oy*KgF#Q.WI.avTx^{xu>QRoGnD.m!NP=Mfa8oMG574h:XW(i@e+clB(4N>Fj)W/)>D4ILYevXa2t{Ffg3blcQ}1pqCkSbf:iE&)UQ0(d)6Y5AenY}TTbdck?+[i/sDlOY-}pFuB/]BdqY4E)Q.^j}:ZtS<]mKbDPVEdC4f?O1VpN5jOP[n7%{{Vai3D4i%]EVNk%c@BQQ$lrqd*C!T}Q7RK2qL4(1j*zI]NR+BWp&kpb*5O%0LQ0vEinKp(ei)+QKcfX=?Pc.$0e+]vR?jT9yZFtwth%$ufz2?dgODg%)@GIjp)T(VzEYq/48fCGzUL2QVnRdKPFfQ5rXt(VPt)=e0EmSo8k&1DigVaa!F-W=Q!]+9*8wpCs$%g[+*VM./dijl85ExPBB3Sal?^J&C&48q+IhvuADarD{nU4Ub7t?Gva^w/Jl[V!l*3zj+f5l83Ch*0lGQ(L{Qt2=fKt=N0qlajIR367515)Erq!P?0054rp2gru/cl1M8$xasj@Lg(}S9/cSGo<4}EpEq^z0eLBaq:siqrcA@CuE=WIJ)]s/U%J@bAc^MI@DhWZT3v[ni8[WTcY.Px0SL=40Gd*iVU2t.rf/S0!E3*#[nG/4/mpVh?0HK:f))QT&-0+ZwO[(=wEZ)Ta$3=CnF#kEO%x]NpfqxwV!6@BHBeY.#*Uz9@!}D[q@e4tQ0#X4+J#7t/5A)Y(l%Ef{W$unfB.ytw:XVT868@oX7l[M%m2)TbU1O(6Z@JVQ1^sfqG?8lyULq4}nkQ@3w3}bAb8P2j1D**&>.kBcVG*rAdYjfKIFm:vcU:.}7GX@.SchV@{]KY=BF*3i$Gbb]AyC$dDg}RT#fjiy{g7Ce(mJ9U0PUzFcDw8MeA[:r?y6hWD-/wN1*Pzf^QT.nPcTiPVh*gb{7>&6abqv.Mntp)fsMLocxZv615GV/9c5FOr%!?eY+m?.E*6H&@R>CTeRY&u=]^h&-DO?8I#WK4{wAHpl?R{86Im1-Tf%Auz5?h39czlZo?PnC6C]&1vpl[%59?6-I.#=prJX>1GyFbyXu)dWb3G[N*y0+dR8h.s.pwc?fvaOuXS+QUROjVV?/GCOeu}xBL>VX3s-=C}Y2}j/y#{ZPL%}w3=)eZ/IrRAo/77799cVH6%*8%a&m.&[&%C#3-q9jlK=VsXvJGVkg=Uu#/rL/WlvV=$fbr?uVEgJ5+$JU-gsfBBN#2B-AVTR/4}Hw>g$TVJo6G]Xnt:TDyE(^/mD9)FhD<{.?OM^%Dxr0{7g{)1@]buK!ZdCV)d!VB[)JHLZ8bFcTH-bIsgboyfbc)x4A)!S%y.2z?OhEAXX=c3h%uI-r/(.Jk=+.3oQU=M{UUmk=0+.VAAUPq6?GA>6>GG]&lcbCf%FImr/o%K&7ojE0=a=IH)Txkz.:O+WneXudn]or#>N4apES9Kg$++G-.Ar%!C>(1UC>N1@%lvDu8LbG9+>!&cOSL[X$mkSO*N.C)][t:9dS3av=x[Kl].^3MWGP-$qKq6vg[EdAFkWruqL>j+O01sfR2yMr(nf>sfwICfFAPq{J*QPIXXv=4bdh)sY(SlTJWWnd(LU{R/vd}7vof-o1TIj98Ao.J{2Vn*GgViq3/PMV}>463%C$1@9-^Ec)$2ZIhvTSlDF-r*HNNXg2M/2Gv}85iO[vG?.JpiL!xxXArw^QY1rpW+EEkFOOL$iZn}pv4wTap%0wIjwbI<@SV*^/0Au!L=WKkJrzw8y@Bv2f37hE[CI4+weE$DCuRA&Cv]JC<1B5}!Zw+6c/Hui0+-epCT<1)i)s0{Yj9j3d&8IU4GdDI1$6$>-fp+mxLT14x3*##7%*GzM8Q-nOBniUGRzTzChiJteO!sJu5La8Shv=1yQgGOKkY[.:6gt7Ax{R+6e/.x$iy@j9icBKvSylZ9yiD>?x*-.b}k0Pc.0M5d1b.)Kr^EjO=sU2fs:}M[#+gCw0x1pb8o7Q0hEP!YfSX^iF/%D#^(F!bQ}$g{rdf>&Re.LOel57IE=oQ@r.1MEC/K]ey=2wVNDAGCBZ(Zo#>m{qbB:pVp[b}8.%1%[#8V@Wkj2pW{Z%pp7J[wYc%4:jdv7+tNWpe4!{>{sD&xz({[i9mWFG2EdgYu+aw=xqp}ow5msHA[?YkOe(Z%QeJ6%}1MAYPmn#Hkw$]2h)UnlFtk3ZW3yf1wh&Qrzqf}/trB)A!d1xKV9):y3D!bAZ8hO[M8H/SAA@#R47:qY4EubkTzU&DBF4e03$Df)naG{{!TJBGzbw/:n%{56r$qLBHaOZ25cFmf3sihr&.]w?JqgH)E568/n?AG*upw3k*7fatDx2bo1c@^E+x8kkF9M23+Z{-&j9z9$>$MP+HX#B/klGJe(5l4%QKpS1gAlIFt}pR}tgi[m)tAF5fkboQJo7a[1@FLPu]X)ZH$di32QZ[oLT<0QqaOy$xVb4lcMj{wtrmKYXcP6M0Izm2d(vQc5pCvw1cnumR{%gVa$RT7nPcg-Nxm$c?ZT.lk:YM>@H<]XoX>n(vjw1bA=v8ce1CXMWX?TRNOA](X1ip^GXvJ&VorYH@qR=.roQ=(Y6{Rt3SWzXQQAGv(vt/-xv1(r=n!C:^nj&Zvs&%=2M2A]X>/94cycENZqkciH/h>}DlE>TO&UHi$r*Nv[DxqgCLAJZ!7l$jHElx2GtFUEAj=a$F4&=RHT+P36j!>Yw@e>Di}-D8JURAzT6-shyB2[BkcjXBHVb3vG/IQ0toPmuVnfNX*.lPj6dR&&?!@phhVek#B@?]t)c@tyH{ZgBZ[.e%/{*gg3d*.WV@ca8a.gv6GkB#3wfii$SlrI0PmQ@[UDSSmn:^{VNGxDAg)u16oW$PYnzz%z6)Z6!t)#QxYD1ZXYk0DSE!U>1LI2hN#vEj@%eT#j#{/+OHYW3x=I2t}VsD@.hW4]scCmvhT*Q?)fN=Cz}{VEF7$t4AtIIZ}kV-<}Pp=SvXu^LtJ$XsDjymwAQK(%W>0)BC@eT)]cnY=(!8NC}.M/yqeo]BfT)9X]&MZdg8dU44F[}[wNv5&?H>EI0J{3BcmijbVh6db5FtA=jE}m?-K}PqcroT=u(E{-AV3<(YXzwv7gb]AK7aq!t23}Fb[CasNt%LKtT=h/VU{PHV))#T^x2@KgeWz0aPMs9hHZOdT]kq5kMuQLr^*G:hBxvZ#wn%>W?}EPz)dM[B9L^8dmET@j5*xQ[w>hv5P=]AK$svJRj0kZttkwrK#am5<>/*pvksH4!Ry>3E^?qvjMt3$pX5@3/xkmtauULfa{w5DZl8*AArKU1^qh%Mk@!8c{+h!Lu:whU370c1X$?3QVXZ)+nQrVZ[0b4}R>Mj)?#6Qi@7cLbl!vboAEk1c=Z/df[tm?QDMNFx7g0z83y-m.F$w0*}&UanR.ag7N+.NnmY2Q#Vf5sNj2Z}d(M9A:dl?9WCfDLn2F+UrIkrYw%::lWw>&2vDrP=37U-onaaZj:z7H7Y/PR^H5qA%)+/fo*)NOsu}Pth)SWYBpAl^s]CH)3?rhe8l:$jVsfnBBYAsw@5Kp11ED9-:bfe??@4T<[fn6L0l2u&zo([dmmwON3ySeo9[4yjpIG<^?GR6ZZ[:pK82gBK]Vi0EgzK7J)5ocCQ7(}ob(]PkfeFQB{0F0NGdhdoTt%f=%!ni@-pNcr/y{tUMJZ[2O.g7Hr}?JL0$:>7E]a8wS2Xb)$Lls(-=OR(5h^GCr41JjmH!<]AU^XoYqqobD^%<8LM76uV3zpH}V!id*f@VGRSJvRO.&tEXqnOj*@lTxMo74i3qg#?EIiX::Yl6D&YSTsVNq5j*W5-7s@O0t*sw-4V9G0}OQ3-hv*kzbXT)pP5ed]U&k*yz!64mT=bCO{N:gtwfhppJK>S6>4+Viz{G70uHo7:Vg5zixmVZ:Z@.%X]Z#Q>BcXiA7bW0Ekm<:&F6fh[7Emurjb@z:ZL)ga+C@auClGQinza$RJ{Ku$^6EYoRb:SpeYPOvmYE)-7z>AS2$61myE})IZpN@)p*)AwP<#Oh>k)a4j%RZsdp4jSIj&PK7Y^z>>r)6G=LrsNWF85?S2t9y/KbW0+hKtH(r?eNa+-LfC]ObvG=O$Ji>Lv5<(=v=/@Ez&w$sUeEDO9)/@4+/ylN$.]oBn2hDUecYDc%PO2K$pE=}aR#ljMRm1I#?WR2r4oa5X].Oh=JeN8lrxc?G^x49ifxVseqe]zYx@+fVU%1p>x[kt4L.nQg7-)?{eS8*mSjfL+1SCZ[GAQgUoqe465DH>ryx$dwkfUv8Js1p!o)j+T?PP[HP^&zVuslPBR0)U#TY-2PozpW]z]xWAP%ZhX[Q:U9n).aK6eJ=pA@[Coh65d/BA!riifH})Taqa0:L@.Scqr@7EWXPx7tpLZS-7*{F*qVk{szs^JiB)4o.LovtB%wDmngFMC[[{d4+@*KFH/1R1W38L:jP)P}=k{81>ER?U^.ZoJcH&jz)*(R0@A}Gr{db+=k<LqPQgyJI@&&#}&hIS/}4+C&{T2GF:b5IIfxW8=xC{K2$Xgdz@zrQloZV/YJ2a7Ur&NP9FnEbu9*2VRoDKukc1oR7y^>{9{z)7OE.<&Mo-5>rL#lD{97[qt@g^8*^Z4o!LD[MC[mN6s>GhngG9(ox^8j1xG]p*TE$IW@X/k)2KwZUQQ/FRoOIPgCqAXqvA+dbhn?dFLmPwlr&:Btc&W&nKV@^Dz-9rQiR8d$O7E2?K:}n[LN0s*z>b5rm:v7Ys))kE@&5}LU/9^nq9RjwQ.!sjcYHAg}JcvvYC>Q8!Z+M=Y/e]^g@U7XKDKoRv)e:q*Q4lchVdRvem9hvq=M7u/)3[1oeV)J@fEOTD+mMu*wb]Ait{Kii1IJgElChRt7!29K0e@A)jm}^F+Ovf+V%(ix]KvuX3}&vHR$YYPqm6eoV5ZnsP[{5Lf04tKh*Hrr&X[<0:KvEc2E6&XNrmb8Y8d@r:+/MD-PX[)n+JTXG0v?]xgfjWsxxd<64p1E>oqVyB%RS.:d9MBr01ZnM6W#3a2E{L>TV.P)J=icscj6mQhhmPtQ4@e*eN8>D)PHdDe1R8ExT[OK%$+}7(l:pz!nMeOrnfu}DS+%-or7QTw?z^gb9Nz&+aRFq*R213CJ6zUw^1cg1LU-/DD?=FAfY?wg^FfGAONrnbUMP:a0}wn6qV#^eg@-<+d[tOwxE=r.fDny=viTnkQGFco82[Z<2(t3ooNP@OUTRL1hya{DyMgx-{jhJ>K=Sm2#TD9ZZQO-5X3T^?]/0Rw:Zn^mp&jcxmfj6>h2aTn+OT%(Q8(wf!T/^!O>M1w/5CJB*JVz8u7b&kGs.2.j9}<@!ED(&N%h-D2&(?Swoha$QTg90Qa8e&g?YeJTpR}Sh{&5a?DRW2ZvM[YU6QlfVK=PhK^YsFrqRHukKr9M3nj(R@m}>cKUtj!dBqt[Bok]$(ec@Ob@756@Ol(QrU<&2KU{:SUJqBFB6yI8)z)2)vh)K>R1Pd]-mEFo[Ik75UEP/rL!k9yx(=ZyzPgzwefjaP}v8O0T2w#qHSF+]Zl-QIBF5sgoM2ZFF5^J$&[:A^2>d(KdIi.F9%YsmZYPz]/6O32A3&K$}MrI$qEPMxYKK6+?cfe9F?H#=H[hAf@[@WgIv)TBxDd=O1nG!O)!jA?m%E:X+Tcc7pJ8P6$er@MCTyT8QADs8(-d6{z!YE[OhPG*g}%0$pa3UN:vfrjRo[.J]MhHKximZ190Y+aD@D/JJ8{G?kCYOaF}D#AfQF+I$lY]O3gS}^9wbii(Hjews1!E[1>)C^DTDO}DaiVv68MxW#=Q4D&>owWxs1V1NfQH7Y&:A2kuf?GF*mx{Li<(}6bpF7WP%KZbYGk[CY[]3PRshj:Ei*z[4ADc*Lo4Uq+K#J.sxz)cup)(7elIFFuC-L}vG?9!ng7XasVHD>aAvH*kD2.M6zJ+o$G{6YGS5(+t@t)hbf=Eck#Ysp[WaHBWeAS&/4)bo3+Qw}1/ISBV24!:&nT+ye0.549rZ2ZIa0q/:ewAyseGna9(g6u*3qroD>)@DS+E9G}:iaN<9Dfjm[JubqXIW50]A#Dx49.B.KB3.D4Y=EBU)MoFj{k(o4*ESgfuDiCmUyxKx3tuv8gGz:48bffQ3Iv2e0Dm]Yv9ZAG>FEdiYt8*#uSEluQ6&vj#P11#Jc+iI*7{%{%X60ZY-z)9]TG(nn1o=e5YQ6^mhM.<*3Bzyfj:B>mEMhNFb8Sjq}{]wu1vtTeIENz*yak){rxQ?HW).PeizXYKI@5]5&kE]g.x>0&.]j>=>{U6[{BUL{oG%JwmUk^G8Mu.Qf:%L/CN2ww4Fj*3}.NlbN98).ADlw/.0(.KTiWr$1LB)[8b:*oU?r]#kl1@&6nE>Cw:k:9aZn&T{=+!ly]a9:SXPd*?K&CH8?sD0C7N9N!}u)Tt4>.)%m8Y$R15wUNd^k1}])qOV<*}@hPoh.{##V{Mpu>+WHQ4Bp4Ju5jOKzE@B@F{U*tV8?Lg[OB(@5AjXAIMl9Ey=u!T/X26jl$J($BTS8]D/G5zY8oX+>(:/.YkF/lCG]UOCryi-up<0+ShK$qp^d}FIW(}R>#J!Q}^.D%M0ru1eth3*4(/MTJ7zx1xn6+}&?7-Z!f{-Cx)e(H[#t4Kj23dZ2P0?^j19HWTiNB)Sj*?vY@*EZo])WKjgfvudHTSI/9DZ:0WpusL}ivSNcZhpiwrT!G<.u)HZkpo]jk5/J2zMSx>B@dzoUJdV7(aME[voG[wt9DTR/1o%$xrU>J)CO}z>i-tEsAgpq4lXhhYXk9>^+mJamTn*y51cs@61o/q{xR:7$dRH77(xf**srU8L]qYURFDxbqk2Crf-AI?YV?.r[0Z03u1E!?Uevx7pWwZw[Izyy}wmI68pE7%6C7=0jBSR{u)N2^yM3o$YUx}QP9162CPh)u*I-utU4Tw)1LAitG1ALFUOj40k8{L&COa5jV21*9-K*&^T#9ii@#oMOT4[:hI}tZuuJ/n)+@5i?Tg%XYi&&i9(MVY{uI6VV7zx1kl.g=^*RCS/B=Qx.cVXm(A/9Mz2M-.aDOG@jK2f1iO5qtQ4pWp/q&E+6twohE!xwK0F2}ya&&njF>1mFN=dYb!>v2.?.@9QH.IKycZmSeFU?2wh$4LVC-y+DIj<0$^P$1Q}8u5@*>mtmUacyC/}y:ogZujDIyqfe*]Fak))epHusVEGTrGZJ<{2ccsbBQ)kETZW&&xDZb.3j0)WgjFM>nNjp.5jyS^{c-W$v9]eOZ^MSYCxl3C=cA%Ce^8rd01CW!OEn))T+0(LG:Rls+Q2-krKLH#jbj#H3E&f[v>0mtoY!@Sa=z&LX344XP]/#[n538dNuDoOLf+VLw^>hnnUlZm!EE>R[^WSAL&!vK(0yl$sQDgfnIkMaCpI3SjOPA:-y>P-VA1I#hxKskOZ-bTCHbJB>%a0{a@U^rK.xoVzr3Ev2>wSp%u{}DEIVKmcVll+qk[!!>Ewz0ryww^^<(^qXjgT6.[Kgr$Z(F>T5xK2lV3aMajV[D1(B-vGVA%1n}BM!=EVg&RgU0pz2k5Q8[pW:TGq(r^KF6)uhR0tro[6Ki^dJ^q>2vc1&T!-CC6vsPN$JO?j-/F%VPQ=DV%8%#fq[OtU#!66aY*jtxjvn?/7ptAF+*39gJuv5ino1%tET*I]tuZyi>sdh#VnxBIx)un#/Nr28FrnEc^au-dr]8T6+BpNy)FbRXV(d?EA5sNU.EI<6)AKKd}299W7g@j{x8$W$k@)y6yL7e]c]XoKS3/5?Q{T}tdOq$Qg@^NTbSUoGu:a(nt&RT3np8@fuXQ!NOU?m{kbCmXl=dVwy+DS]3BI)FLZm/h/yEB@eN4/HgD--?LFow@chELmX:&v7SypP^>tkQ((ua1ob4c9>0dEa}dBM(LGjzp2b3h{EO/L9WdZRt6m%n&6^WI[+iY.]s])@&B@q%0a{.@A5HQko=5@@pSpW8]Nm{mH)eNSZ}%T++5s$UBPC&7aa)*)j[sg6OqXFke:TtKt^2bp0lMOvEe-fsIn5kFvn}FM}+f?aIYgMMzL+YS6>wbD=eHpeiDSN0&yGz!G]>7wtQ}+B>9u.7v{crgeHClC]Y>iJuS#707G8&9NiQ9QaBvjO9-$}Qmel<1gDR@<4Y7nc)sE}ry]Y5V4Xy{@$Au3RwvTO@:.Tl9hX!qJ&{)BkkBj>PZIopX2868:Q+CHxQ>/UN&seT-@&TDT$q7EgbLP:Y=T-xZ-L}9Cw*Cs^$5iX0kI)[JT--=RYq93Nv({BOOP]Jb%fC[YRN[*&74KeZpcM2ZPd?usv.oP7KQnyAbPZqsLI]DB@9Cmx])3bKP3gBEyn6y>]i9=K.nJ!#Pz9p18r@@G=i[#Hd{FnFJ&c3f-}Q&)rM*JB)gG}*s>G/Nd4fC03lVGz?I*v2oYG}g(tF?k+5B#KazpD1{YPAAc%k}U?Hi}%GLwJ@!vX-0%qELQG9Fhe>4LA&h+hj6{Xl7)/iEag8mG8(&.su/@2]hevBwIA>t8lq7*?BUfrAoS:(nWl>9YYVo{[>yug&:K[g#LCfmFW$k2#tqpJg=?x9zeGTKH?LhETsY-9n/b0&Bu%eQZ?VH!+FXdG)QM=^3]5sv^Qcsb*)WlUjnu?P=$:^G!ijt6o8ixz^J2e[1q#TTb(SSGZmGU5*dxz1v[hSuMqY&Y9v^mmTcdyxF2>T&u{BJ(Z4[ooaJ{mL7R}L%[2fqOky]*mY^wNI{IVJTt*=TIqZ$@:aixdZUx[gxlb3^(}*PHqu:FFZwmkuHC->%kW(FyJdgtSIx%qPPELdJELOj+i0XS$9V-zJ)Wa$MI>5&grI{/W*Cnf8*WqY/6&e=iMm$v:O)bm[)X{4sN9Ljo%XjNw(O].mWhiJu3Ve/[gR]sDgSj7pQ^f:n56bl}z*5n$Ek47Feb%nKE(8=jC/!F%{id1@isHDJXyP[MfrXK]b?koPO5ztqzS%+n(Cr*f)$AinUjrdMVuJ!OyS:s*=fYqcVAq9*Yb3LuVIO3AbY9uF[yCc5oxo#$Sr7M}El1x)M8AmTrxgAw7xCCDuIVUsW]ey%H4eZ3*)Xo(eL%NM}tAn)nEq)SQIi/BX8TG5nz>M8y{dCj}Y!S(i$TLJ7t)jd<>WT(>9$?V4Uz*xOI+=[fE!(*Hos&W1zUIuA2-xd[fjRq4dz^5&.jERW(l$BByk]o]aLQ8F)pNQvD&XNqT(fW0y!=CajMd7Zxg0bNjBShPj**f3u8HUyYT)#2NdMFWu{}/@TWOpN7?k8P%LO@.8*.:L{j:[XggZO}oSR#Xd7%J&5ARY&9:AwZwjSyaT?^pI!AIMR=j]=HrsQ@d!XSr2itIq?SN0V%hj4<@d]H^R]I^y[7BA5wvHo5?G:^n/zHI=%uLwW)LmU0AcFO+8J/N2#zx5Lw(>]3^lMSj0nN{?>qe2=WeBZNgdDuNJPwDy4FyO6fNb4=S+LZ+$PNYK0u@aZP#JKzK:1T8]ZK/5W0bK5>M@tU=T6Z6KJFZnew[!6i{&1yptSadMTUx3oJ9w?H{b*dyuJ1<{}sSzcwgD}K5]^wV!LkJV(>4D)py=^{^cNj2+X3+w:^(Bk]fRFO8t$5=#^og-(:A9^p&TH95$^=>Vf5?yyxj-[A3wB?CKV5u!vjF2RImGk//>w!HwsgRiW4vq.>cTXi%L5i/tlhq*h{1ze]&zT.77sS/})w/:RhqWNk3K>SHr3Ah%E^*)X&sOze2.rKeusX(y8S*qSMKt2P@w@HYBnkEmY5>t=EUK=INhy})7l4<(iWsfi>LhWeG2X)wc@1ubC!yy259c%?QBC.L#r2(gcI(moMq/vR-8f2+kK(f7wF%HPaf9z)M622XPHxxd#r@Q>V$:P{f@+W5XhbQ8q:UnhBWTB:SuN[x/YPoaVHNtOxhJ+%8l1hsYl:%wP)3A9]Co24E*Z8j4N)fW<-zlxmSS03v6NnA8^zGg1DB?5?a0:hS7U#i87c=KmHk[qY+Z?y^F[SU+Diqn8qg%UFPG.g:.>kuDz.4]7O#B8TTsHeWLTn)0Dttw3np8rAqT(LA^1s*p1Ox3(q>kxR+54@={D91Q[&XufWVbFgktYX/-RQM5WS8DNcWic>c)blYkePX/yl8jV6v:MqhMOLx*wNt9Tf1iz?3dSwglmpp7cyt}fqv>zqf%fEm)^rQICZOl]Givy#=-8UC8/$T?K3fw51Pp/W.NK<(tNy:&SY/:Qo3lF.:Y8)tb)Uc3/otCrtnYug93D:DzPkjVdd71t-I{q)Z}7oB5^#pC[Lf%jgQ3cP(cqF5Q[%=c(Z^4.y(3LkvliZ-RzE2sCOwV/o-pWy87{{p2xA?Qqe@qX[d>*XdkTT:KJiVGS>5Pv2d75HjSQ2uODor]Wfl]V3ZgTML.N-SZrSlPV]c)a#*oFtvA42/h[dVXNkUoihMeC--Szr7%{k6+yWxu+KBe(1%}4K{+GV0(:ogF4Bt*v:(MP:2%{=RTXlOcvkMoLlm)k=Ol9^]8I<-O*TCLs2J@1?{J>EzAdVMf1&Y?1dbY2)Z0P#tGDzE@vG-?Ry]UySquwg/3ob1%[zTBC4rO$[fOw+Zq]8&TNXD7^2e1kC-sa6l:6:Z>f&#j/{dq4p6?rsAsk6.LByvv(gOLwNb8ah155})%a.RBc[EDR!o>*7c{K#BGOyC-t@6GmZ^z-)A4id[w59Kq]eV/&)4-tEuK/a^U{{!drjj3nCexS$#Z>Sm?:ql)+#pDEv=)O7bzvpNB*d=$cg/uM[H^T=?R)D}/Q7@mGKp*y4EVH+Yjaeo}AUnGJ]ATz>VABR{BP+SDl(A+ZN(wC7ESv]!eFG+}o8O[l1Lrlr.t#pmQppPYxVGDGEFq@)l5Y7&i14c9gF#^Z24Y77ouhV2PC!W>6oyWAA.%)#o1xG:&fxe.e5*.CX+[uDyOJeGepafSL&Rw3hSy(Az>?619dGCO+rIlrkRf<03I2a]PW!%vIK[TI!32vQT?h!F?tR!xxSyvX%JxI#)qclM*GvIo#IhGbsxpy7zerWMj^BubPLIesaXR*)i0djd*}?cO3KswT$w]qG.P^zJR>r8A4ZhxQ0RFyuxRQ1s!*6X7fzw(o1]E3IV63Ca34]JfZ*wW2IQUBsu/h=BEwA.Pps)N&dMq7/u3nVdI([JrtCLCRxtz.f79}MwNw@qyssHLeUY6v:x#.XyQ7Vpiq+^wbA5pq}B-X>(BcFU#Y({=YyMxu[yz6Krh4tl=2amAeLz1mHy=i1LTmz}Z<&mo%T}}}aS0f9:=*-sR@&>4vaV{FLU34)2}IR3&Ow0UP+KM8KZv{.K?j)2#&%X*1+%:HB:hC]AF!%9G+wMd-IMPNJC./yLLh-PPa>U:Zi@]o]$&:}354PXaG>Th/%QjqG]PN.tJD9z%}-.@Eo*1Qe4Wi&j-67=REkes50nH2b:Z24<>LB?@:M*g+8d%l}$4CG$%]gTf?UQySpyB3qT}rZ50lwqNSt=G:RWfWxxF^QmyBF9XupU)A0?g+ei^6HkmSdl//ePq5yY!t+sMP]!hw*>diVBvx7hJBp270%rfEtxJAss441(=Ic]O-Y?G@T=UZBBVn2!kX]Bkvb!V{[gcLh>f%^+82DbP9%zSS@u9mMEvvsU}cMs]xM+VYYqzI=h4GGec&U!RK=odi(+Wo^7[D^6?$-V?[w5xEJj[Fk![g}V/F{6WnR>ELrr*qv)M8}l*Q}4@9lLUAH7XcfEFBYR!<@thMgU%+4ZepJ@8A)KNInqk/6@AdV8f+>@RzLet?I=C5=s]o9rtPNZ<)oX-5{Ytwxe)92phFQzC7h*&Ti7FIw9*GL#P]NK$pL[wD6HxLo6]YR-QJqjXb!Bjn{6<9K9O-bB6^0Q=S}TJJvkkyHj7XcqC4BN16&@b*E1w+suYcf&PMj+s]o0y@fz*P3Vs2}/TBv1k@?3}C7Yp)#o%HF9&zFR0z3v?HP7r!F>(L1alRT8aI0)3C.wO5[%GhNA?sAhb>TvXvz-gCfrSI<(o/l)FUKRopQU+g!uqsCCKG.+S3Q8@<9nlj[3Y=4s(vTsUZ.+h}Fm57pa{yJkRaI+6GTAro-Y.oAeecmpuAHJ5?FD{uOi=esHxwrBssj>6DK&Xo3m@jq&reX%d{WtU?arrT&V1fU.0vwL@%ogtZQ{rz.PMg]Iwbt$aHo>@3CaaK)pY#xf>iG&6.m:i6+kKW$UeY%XYsUmJ/wAvIEM&/f}5+*$/x68J#cfTFMuzwvdHzTXMv3tm!{&*InHFHQ+Kyd./f/%Rh)Fp+(m]-%4#n/gn):bJ7B{DvUl=56Kp@GLhif^ILe+w>1}^bN:DuCg^ABIi)n0=(wo^pbu0:qh&Z5{#Ssn3yvf(6SgL#MwU+$gPXuFaP}u+fg<3Koc4p5u04}=bJgbCrK=sfi4Qm>QnZu>61!LLeK}jSc4DLf<8WdjY0*1Qey1L0&e=sLs(+r)Pp9RRJ1NQl+^17dlV-aO7]1Ho8B7f)>Rem>N(xn)s!}4T<8FBGS]@M$>[DAd<+W4KTMFHxm#jfA^:hvj=)tQAg{3a@(@.v#zLI81D&[xDo05DR[>{[7.F/byW0XpV3q+UFCBNED?!%NUS*Jd-wl@#IuB2tF(3[^NyiWpk?)/rD-BYWwMAp[p2T3]rCTKB.bhsMQ3PC#Ewr[fWqGpd@wxpAi/zKY<6i<3l5:Qzp4BMiQ+B]^A3145pPCkEPC(7bg*klQKSW.(?onlYNkU]DSD8)bdi1%61!SFK/)ik.?rj[@mA*sF2#Pf^/Ps4v{lomlAjT$k3]WKQVbfDX*:=ZJPL=u2C.D+bExlhvFh.)w4[hw@n]HCPTj)FW2{LkrkZQoY(0h+5yo5R}=+Uwj#>20m8mTE]HsBwIsVHFFJ6wo-:![w[sy^sBrRdC^f4Zv/5(L^zz-P$VK1kHbLO/anJ6$23hmE14bMp0GTp3p5shN08GLd[&kQ&O2ox}!@JbY.cgwJc&1jMS.TygVXY5A]v+x4JJ]zC3*GPX[oWLx2==GBC]6+Q=sLPPffvB.C(XLoSlyUWT)1Vi0%X7NY#Q*]4YT!]}Em4wo}-WknF-Ucq$/rGC$-Jx^w}!*S^[Z5LKH.]R#Oim7q!jE6:=@&ku>Gci!]X5YQXc+Coyn*z{^CZTg*zG6N-*61k)w@mi]G[HcY6=JW1:A%o>:<+#WNclSIYojJ*C%z4uA0:u+5fkTX$*wf?OZM/I}Tfg6.CK8e&n-tO^uy7o?^*UpbJI*8ff9VC0rbeaZ1)49P1+yqx/^XjwLl5{&ZLpvItT!P8-2hdB>JZyXEao<)ulk/ArjHJ1LU(1tl)5K!z8}k&4Eo]E5?BWLmt{faw2j9J%^m%pAX68E?8@nfzF+sVh?t6A*A}PY&5:oKL2>[l8+a)Y&&wHDmi?(=SJNvMkQtc]T:r*<4]?R5G4wG:F^+[Wj]:0sHxfL[j0DZ=gnBG%>L=AjfQ{>QlfBZx>F8OF-E4U]^N&1(kiXx$J)^!&$&NFmoxN#SW@&Z0Z8z!d%Rot.-EL8b.a5[!q0IA^%e6N?CclR&(C>7jTuCYfPtGV@AO/YY8Q=VF!8Jes+jMPfWK6[[IgVodmpO4un3>g%B@E0TN>N6}vKJdB2ZFCaN/(WlW:-KVtFl5#l(vZE6E%^UWo7=$lVGYvYjQqmbCvLg@bQYw7&bVTr#aJ<5xBSSXD9yE53hYfdPIX9yNZYBVbby3lVO3dYbLlI&:Mj.Q:1eu9(jnf6YR+6RydFGWBqKyX1LR0!gkM#ysMF3th&wbde2+XOSQ953G8CL)>{//*dipzpVC:nK(4f/#vaCy^]Q3?&>Kg6L427l4&RtAwB.#=8sdn+alf#S[B{&AP%bGhh0Msl>wnzi]AdS:GaCP[0DI2lj]@xit@imlhB75ZhcY!9y:KMt.9@#5y5N&T8>Lxe#X-dzSu}VDL2qJ3b%0LWuC8bm8v7&G^IIT=%)7}:FPsJ-CAg0NR/+7%-:hv)ZQg:0Xya!ISF-R!MShFaPV@j&N<7w:sZ2aDx>**H7oi/.LAES*yX{/Cr6-$gQ.&q7(^7$09)h:pskw9bgM6eBm(or[(@%lbW.!euVa}!ecC3%w[]5b}x/Dv!q=(-@wLSJm#n5aI7?)RR0&/Mt5d0QWXmCM-r!fSublc@f-te+4*+J+5S0KM?TnVfCpti5jYVKVwDh/wuLD674f+x^MtXzK#mgg[Ww*{1GfL)j1^H3LA@zy*ND]juT0p5E{-bvOE:hpwOv93&WG?tN>jmp!#hw[4r1{tSUXrMtdHhL2?Jw5zgYaD9hN:M.TefJZCA3)Btw%5wI8NVSQuRN:6x3O^t:dNkBa=$1x!})mZC=tl@+IJ49UDVn/iiR[H.(5k/U#V5ESE^uf4yKUf*%#b!x)t+1[0AXkxNB!38{XsfynAkEl=7bmcCv#r.nTZsxu2yBr55{f[*f43W$lmV(otoYGIMHyamwg{:kB[bxne{2/+WWrlW($SzIeLf^%Q2ZM#vA}Yk.e(#3&*5Rwy:Ci7T*N9Fiv>H[41EdE+#+*O[x7$X&)CnTx&B)*9YwU]oK=RHxkGJbzW/S:?C1!!4JDZ[hKVud^D*bXL0Y*J%WGSH$/^-l@qzM!ILvHgDO-lMy0vy03o8BKFPe&Vz$nK.v2S6*-NBP%ZvEbtkz}fAJUWWA%Ato&h833lP]yUv%p-D=eR=#zf}JkGhvnx!4o}v.y1X-a6?>FdUR1RjpB+sxRszvY7y18g6oH2{ii/)]0ku&Wb*yDR13<=*3g3t#sx4{mi@>^wsplMwpJ.d8BvQ[I#<>A%3UaLu=X8r^Z#w)=8w&10EP50>6Z/A:N%033TuA05AI3Grih).Qg%Fgtk:h$dZ:ZYl[<4:+*&86R-mH?m+Kg-[qst^LL?m*ExEBoI&xd0bY9P)VFLM7&POJY%cUxC:s65(3i@/CE?5c*VL1Je=+hIXTgcnh{ZPKDHYa3f}j}x}v%8&??#J&&n?qk+Kh/LnzUdA!w67O++aP3p.8$*28^Yn[6/AfCYDD@J^p4-v7o@k>U#D19CozAI=?OS9sDju^=+{D!)m5J4rbzSiyVc3UJA24wX]fq5tflQ9%TQy21f2fma}l{bL]?9!:16v.$&a}Zo1WxQ:zHQVkvi1T#!iK/jb.992Kpz0xFIoEqKVwUj@&uT+OLH(Q>DnYY>Ij0WORN1PKmt6:8fR1eB@K8stxo)vW9U-xQF20g*[3z}ywFY@k4zQA(Un5H0.Vl7C]VUEW%yinMq4Bs2>z06h]GcfXD@(IOIGO{c6i]dHPo6F8=lcHdC-1(+%xt)QnsBp:h1XxHFen.j@>fh03jJ>5*7JM@?f1o0p[@L%Wz]?aEuXgSaSuO!fChi6Uye.PteMR+#w+w@Hr2hrL=)r0i?tLt#>9tnODwQHQKl+0=Zw:@dyCYpPu)v1TJAkqh)=*6D8iZN&8s@/7-^cQ3[ZCmsyFoFrVpoBTaji?yg(]#*r:oyMa+9m(exX)(Kexu}[bp&hwv3.va38GCX?3Qyg)p3^X@Z]OT@:%urlJm.WAYA]S6+d/{42>XdzQQlAk/s]VG9V1dPjTpqtf2x9k4J*H{Jf<*$LSwUw[oL1QQ+?PYN4wKcIA:uoQJIAafZ)Mk^WY-N?j32Ro)GdE({=NJ@Tf]SM%WvJ(wT?7?NzR57D??Y7a4vocLT9?7qQAu4bUccTfbl??.}Aw^i82-&C06N%E?C%/gQMrTcg]bLDHj9M^jBfEpEj&Ntw{ZCJvpa)z94}gSUDYsJZB&FrAFU^khvP7m-mV-0*[@o)W*PSmyetb=ZIQgrrWzRHKX/!4Wkc[)3o?Ct5LkXN@sCDO}[(6b8.m4wr1!V+3BsXZyL-7Mua6C8-0.TuEJdfW@xCtYRIhUQ[9/2:Hnc#/sFqxJUQ.@80l(bcntlro>JOhYn7((%4}/T6Q/)qoSI&8&4*D1LrJwX4>5#OxPCzU+6P8pJKpvbVPJ2m+vgbC76IPf-l+ecNl?o>M0P!sr7)AC>!XI^8y2>+X=bqier>>lvd7@(lT@U}yZo.:4QlDOeoJaBxIl*3w.Js@pFrYi9zCz+*q[Xgv%<*)^=Z:iDgs?N]RyeJg{W/:yulCwwqP@L1$g]Xlg]FGgvP:4oIQz]Yactu5wPN3:byb9>Flt8cprl$.WovjlU2A6[xp=2+:*cfVet&3Z}Gl>}$U4^4}PZ)$JiAe$W:&*N1j(F%B9[orz=f6$$G7*]ztCl/ezj5W.DV0O@wg%fYu?}+i^xrTs:X/0HD%6iB?S8V0gIrz0+@-KszO$QLcEQ7l+J}&(hP=wwY}+W9t*mca0p2AO@wH/IVVeVx0vvk1^]b>?e0.S1FrDKkwMZ:Q/kj:WFnbPRVmBIUia]y}3Ut7AVE*cFjnDTi!7&Ur{KOJwREu0-SAFA5(!&m%gF{O@EAE>9dC}$qVHR-i*{RTPf9BI!}!-)jl5KiV?OsfgigSBbf}AC4@Lj4?]*pmEDE)+M3aH-nCI>^iT]D^ggQE%k?u^Q}z0F.G4MT@twZv{YO&J!1=Qhuspn&U4Me67J^fT[jS&N$8zp)yDshN&mjBc&:Teo{9BbZe*y=4rH:9AZ%bDBVy!vb(UV4X4vk7k!ip:Mj(?Ad1wR&cVqIKbfzDn@6xsqs5Lyax7/D%7Evq*R:H]{6yDUqNJq)Urqn^[/L*F3PlP@eZ@5m{@+)D5k>s2}?W0P{M^]N.:AC1<={ZEG)CuEJZrZC7wsDpDEsdX=MBYTX#?cSeq*7$Gg?O38L18dT:RYUnvhl@Qb?&q}Uq-rW$}ro>7wheCn?l]6kM8=aOe(6hdo0K32{P.P9lH}r=^RLn8f/zWz1^hD-}B4I1vLpeUuuWu?n5l]gb}ZhAiyZb#y6[Fql13Xmt&ldi4!wYVXjz.M7jUaXp>7x0Dk1#x%iMnA@oWHf-X5*khshxcWoQep9J]hZsM-MVBL*xtC%9A>6CKi#TpQ>v9O.J$5vY6..4e&50BA(W=zRK$Y*p&jsS--e}yYrGjr>5UgE@TNz(lcbEmsQ9nA#vC6MetGNzwFG3NM5?{&x26i&[x*Ia:x1cj2q*VKMxf^X1Ju=aPwM!6@pu]D@f.U:P{Z3mFGtS>Oy+}Jxu[#F!aKXI2K&j9$Ensrn>si.AlPiE98s8&^x95.*!R/BGUKmT%!]kRVe]EaiWK^[PK/&!E/1cwQ.17]K<%ho.:aOH4AQ=ZLTE/fJ0n4G@Z:3=ZDMVE)ch0r]Db*@soXe5nCd+R}Y+)=gHVlV&08tgGA({jo5XSRZKQC:/0)q=):bFC9t:{@G$}2opF/v?hj[]=v:e=u/?P3Y=s@fy8$bTApPsZ5h3#>IX1aN5Ta7D)Oj>k4my^9:aQ+0O*hPkzU{1*KY=X/m)wPrHr6OFhP]?>IBEC}$x&%@C%]GBE{y:WI]vPFQ8h0CJoAUzDBQNkLG&0lM5fS.ZykOB2Gslk@?HiJhDxQLa]:NLJ)5o.:tuJ8EP7ni.73v}lXs0Qf(#ia>40J(i3uC^UQqm^ZaT<3Gnk8+5[FwPx]0]IqO2L[wADgjkwM7P?NHAw8K1)lt.Z/iHX$0:].z4x3t>(A8o3yow3b%DLJ$/O@c+P&Ren9@+61%4lzSTIdVWGFMsAJ*?/[BN}d#*Nvwr^0D708h+Zk*%YVgo3-v37h)DZ!EJVWPFUvci1J/a0R{DXe>D[:-9rQMxvG-1GFTHquNnf%*$l2(urebKut7f!a2^=@:23A4CF5l>u>-z(ggau/rj2vC}g0>c]-FeeiCaidMO[meZr71=d^EhYfpSu].DXZz@0OlFbGlq}{jeH%aOdm@r4Wt.Y${^<07-@dZH?347oNnq+IWxaA[&wdMn%6bglbo[S7PDmLep4D/]Xl]6@kkLun>)uYZIY&GpvtjLxhS><=o1VAgGKY^:US[F!Xs@cZIpU.=EfeherWzid<6.D+uO/}Z*z>pHf&c3R{Irb:7xfI}YT62(/B*&{+DVMdC==Tar{xzItkieQ*]iIrr!@7D>WH=KPoPELV?Hc]&78$!1vfX$*2[Bm=La$3=KYWxK.n4moJCz/mwR<({Or3({u.&/4sziDTRSeKuvuwXvhA0A)[yBcz*Ol@i9/ft+NUlf}(eCQ*fNbk!knVxse^Wdc$OMsUx=C/R?0Pj2hHhq6:fa^*=64j{jMw-2q{U1[9cS!kr1#!pm2:&{THkkNO>iCCAG5p?lZ?stc[27bKgKNGiAl4-MH6drZKxDA.)%1QR=A{LL=3Z=>IP(FQ]MXHpgccQ*=!k6h*e+zYxL=%(Z]LLZ>0fg*t=mvHY*)CkAwz%P!RFG*p/LGn:jW^)nR8c}(gXzfuaEiFpO>bfy$:52o5*6z6nE[%>Q(1ss3p.tLMzU9B(p^c}!y4)gJj?6{8*LbXd",214412); +export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4fc4ce0..b62d3ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -228,6 +228,7 @@ importers: vitest: specifier: ^4.1.2 version: 4.1.9(@types/node@22.20.1)(vite@8.1.0(@types/node@22.20.1)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0)) + experiments/css: {} experiments/ghostwright: dependencies: From 4a793f4b67a5ccabeb5e66a2fa2c09f15e788937 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:22:56 -0400 Subject: [PATCH 4/9] Add ergonomic API: top-level compile/compute/computeTree with disposable Sheet --- experiments/css/package.json | 1 + experiments/css/smoke.mjs | 91 ++++++++++++++++++--------------- experiments/css/smoke.using.mjs | 37 ++++++++++++++ experiments/css/src/index.js | 77 +++++++++++++++++++++++++--- 4 files changed, 158 insertions(+), 48 deletions(-) create mode 100644 experiments/css/smoke.using.mjs diff --git a/experiments/css/package.json b/experiments/css/package.json index a37dfaf..8e48d35 100644 --- a/experiments/css/package.json +++ b/experiments/css/package.json @@ -23,6 +23,7 @@ ], "scripts": { "test": "node smoke.mjs", + "test:using": "node smoke.using.mjs", "bundle": "node scripts/bundle-wasm.mjs" }, "devEngines": { diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index cdeaad3..38adc80 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -1,8 +1,8 @@ -// Smoke test: drive the canonical ABI through the package entry, against +// Smoke test: drive the ergonomic API through the package entry, against // the embedded artifact. // Usage: node smoke.mjs -import { component, decodeIr, NO_PARENT } from './src/index.js'; +import { component, compile, prepare, compute, computeTree, release, decodeIr, NO_PARENT } from './src/index.js'; const css = ` :root { --brand: oklch(70% .2 40); color: var(--brand) } @@ -16,20 +16,17 @@ const css = ` } `; -const { ir, diagnostics } = component.compile(css, 2); +const { ir, diagnostics } = compile(css, 2); console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); if (!ir) { throw new Error('compile returned no IR'); } console.log('ir bytes:', ir.byteLength); -const sheet_handle = component.prepare(ir); -if (sheet_handle === null) { - throw new Error('prepare rejected the IR'); -} +const sheet = prepare(ir); -const sheet = decodeIr(ir); -console.log('ir version:', sheet.version); -for (const rule of sheet.rules) { +const parsed = decodeIr(ir); +console.log('ir version:', parsed.version); +for (const rule of parsed.rules) { console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); } @@ -44,7 +41,7 @@ const parents = [NO_PARENT, 0, 1]; // this fixture relies on the width. Compute each node with a dark viewport. const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; for (const target of [0, 1, 2]) { - const computed = component.compute(sheet_handle, tree, parents, target, env); + const computed = sheet.compute(tree, parents, target, env); console.log(`node ${target}:`, JSON.stringify(computed)); } @@ -54,31 +51,36 @@ for (const target of [0, 1, 2]) { function memoryPages() { return component.raw.memory.buffer.byteLength; } -// The tree API must agree with the per-node API. +// The tree API must agree with the per-node API — via both the Sheet methods +// and the module-level functions. const perNode = []; for (const target of tree.keys()) { - perNode.push(component.compute(sheet_handle, tree, parents, target, env)); + perNode.push(sheet.compute(tree, parents, target, env)); +} +const wholeViaSheet = sheet.computeTree(tree, parents, env); +const whole = computeTree(sheet, tree, parents, env); +if (JSON.stringify(perNode) !== JSON.stringify(wholeViaSheet)) { + throw new Error('Sheet.computeTree disagrees with Sheet.compute'); } -const whole = component.computeTree(sheet_handle, tree, parents, env); if (JSON.stringify(perNode) !== JSON.stringify(whole)) { - throw new Error('computeTree disagrees with compute'); + throw new Error('module-level computeTree disagrees with compute'); } // Re-prepare + release exercises the slot lifecycle before the loop. -const other = component.compile('.x { color: blue }', 2); -const other_handle = component.prepare(other.ir); -component.compute(other_handle, tree, parents, 2, env); -component.release(other_handle); +const other = compile('.x { color: blue }', 2); +const otherSheet = prepare(other.ir); +otherSheet.compute(tree, parents, 2, env); +otherSheet.release(); for (let i = 0; i < 500; i++) { - component.compute(sheet_handle, tree, parents, 2, env); + sheet.compute(tree, parents, 2, env); } if (memoryPages() !== memoryPages()) throw new Error('unstable'); const pagesAfterFrames = memoryPages(); for (let i = 0; i < 500; i++) { for (const target of tree.keys()) { - component.compute(sheet_handle, tree, parents, target, env); + sheet.compute(tree, parents, target, env); } - component.computeTree(sheet_handle, tree, parents, env); + sheet.computeTree(tree, parents, env); } const growth = memoryPages() - pagesAfterFrames; console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); @@ -90,40 +92,45 @@ if (growth > 0) { // pointer from compute-tree's lowering, so the option discriminant's padding // bytes are no longer zero. Reading the disc as anything wider than one byte // misreads these results as absent. -const hot = component.compile('.card { color: #fff }', 2); +const hot = compile('.card { color: #fff }', 2); if (hot.ir === null) { throw new Error('compile after compute-tree reported no IR'); } -const hotHandle = component.prepare(hot.ir); -if (hotHandle === null) { - throw new Error('prepare after compute-tree rejected valid IR'); -} -component.release(hotHandle); +const hotSheet = prepare(hot.ir); +hotSheet.release(); // The rest of the poison matrix: re-preparing the ORIGINAL IR bytes… -const rePrepare = component.prepare(ir); -if (rePrepare === null) { - throw new Error('re-prepare of the existing IR rejected valid bytes'); -} +const reSheet = prepare(ir); // …and computing on a sheet adopted after compute-tree. -const adopted = component.compile('.card { color: #0f0 }', 2); +const adopted = compile('.card { color: #0f0 }', 2); if (adopted.ir === null) { throw new Error('compile after compute-tree reported no IR'); } -const adoptedHandle = component.prepare(adopted.ir); -if (adoptedHandle === null) { - throw new Error('prepare after compute-tree rejected valid IR'); -} -const afterPoison = component.computeTree(adoptedHandle, tree, parents, env); +const adoptedSheet = prepare(adopted.ir); +const afterPoison = adoptedSheet.computeTree(tree, parents, env); if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); } -component.release(rePrepare); -component.release(adoptedHandle); +reSheet.release(); +adoptedSheet.release(); + +// Disposable protocol: release is synchronous, so `using` works via +// Symbol.dispose and `await using` via Symbol.asyncDispose. (The `using` +// syntax itself is exercised in smoke.using.mjs on Node 24+/bun.) +{ + const scoped = prepare(compile('.card { color: red }', 2).ir); + if (scoped.compute(tree, parents, 1, env).length === 0) { + throw new Error('scoped sheet returned no styles'); + } + scoped[Symbol.dispose](); + if (scoped.handle !== null) throw new Error('Symbol.dispose did not drop the handle'); + scoped.release(); // double release is a safe no-op + await scoped[Symbol.asyncDispose](); +} // Diagnostics surface through the ABI: an invalid color warns and the rest // of the rule recovers. -const warned = component.compile('.card { color: notacolor; padding: 4px }', 2); +const warned = compile('.card { color: notacolor; padding: 4px }', 2); if ( warned.ir === null || !warned.diagnostics.some( @@ -133,5 +140,5 @@ if ( throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); } -component.release(sheet_handle); +sheet.release(); console.log('smoke ok'); diff --git a/experiments/css/smoke.using.mjs b/experiments/css/smoke.using.mjs new file mode 100644 index 0000000..5809558 --- /dev/null +++ b/experiments/css/smoke.using.mjs @@ -0,0 +1,37 @@ +// Smoke test for the explicit-resource-management path: `using` / +// `await using` auto-release sheets at scope exit. Requires Node 24+ or bun +// (Node 22 needs the flag and does not parse `using`). +// Usage: node smoke.using.mjs + +import assert from 'node:assert/strict'; +import { component, compile, prepare, NO_PARENT } from './src/index.js'; + +const tree = [ + { tag: 'root', classes: ['root'], width: 400, height: 100, state: 0 }, + { tag: 'card', classes: ['card'] }, +]; +const parents = [NO_PARENT, 0]; +const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; + +// Sync `using`: the sheet is released when the block exits. +{ + using sheet = prepare(compile('.card { color: red }', 2).ir); + assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); +} +const pagesAfterSync = component.raw.memory.buffer.byteLength; + +// `await using` also works — release is synchronous but the protocol accepts it. +{ + await using sheet = prepare(compile('.card { color: blue }', 2).ir); + assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); +} + +// Neither sheet leaked: a second compile/prepare/compute round still works +// and memory has not grown beyond the sheets we hold open. +{ + using sheet = prepare(compile('.card { color: green }', 2).ir); + assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); +} +assert(component.raw.memory.buffer.byteLength <= pagesAfterSync + 65536 * 4); + +console.log('using-smoke ok'); diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js index d7bf4ff..131ae40 100644 --- a/experiments/css/src/index.js +++ b/experiments/css/src/index.js @@ -2,12 +2,14 @@ // // - `compiled` is the freestanding wasm module (brotli+Z85 embedded; the // module is decoded eagerly at import and ready for synchronous -// instantiation). Regenerate with `pnpm bundle `. -// - `component` is the instantiated engine: `compile`, `prepare`, `compute`, -// `computeTree`, `release` — the WIT world `bombshell:css@0.3.0`. -// - `loadCssComponent(bytes)` / `loadCssComponentFromModule(module)` for -// hosts that ship their own artifact; `decodeIr` decodes the compiled -// stylesheet IR for inspection. +// instantiation). Regenerate with `node scripts/bundle-wasm.mjs`. +// - `component` is the instantiated engine exposing the raw ABI: +// `compile`, `prepare`, `compute`, `computeTree`, `release`. +// - The ergonomic surface is top-level: `compile(css)` returns +// `{ ir, diagnostics }`; `prepare(ir)` returns a `Sheet` whose +// `compute`/`computeTree` methods carry the handle, and which releases +// itself via `Symbol.dispose` (usable with `using`) or `Symbol.asyncDispose` +// (`await using`), since release is synchronous. // // The embedded artifact decodes via `node:zlib`, so it targets Node-like // runtimes; browser hosts load the raw module and use `loadCssComponent`. @@ -17,3 +19,66 @@ export { NO_PARENT, loadCssComponent, loadCssComponentFromModule, decodeIr } fro export { compiled }; export const component = loadCssComponentFromModule(compiled); + +export function compile(css, origin = 2) { + return component.compile(css, origin); +} + +export function prepare(ir) { + return new Sheet(component.prepare(ir)); +} + +export function compute(sheet, nodes, parents, target, env) { + return component.compute(handleOf(sheet), nodes, parents, target, env); +} + +export function computeTree(sheet, nodes, parents, env) { + return component.computeTree(handleOf(sheet), nodes, parents, env); +} + +export function release(sheet) { + if (sheet instanceof Sheet) { + sheet.release(); + } else { + component.release(sheet); + } +} + +function handleOf(sheet) { + return sheet instanceof Sheet ? sheet.handle : sheet; +} + +/// A prepared stylesheet. Wraps the raw handle so calls don't repeat it, and +/// releases itself at scope exit: `using sheet = prepare(ir)`. +export class Sheet { + constructor(handle) { + if (handle == null) { + throw new TypeError("prepare rejected the IR"); + } + this.handle = handle; + } + + compute(nodes, parents, target, env) { + return component.compute(this.handle, nodes, parents, target, env); + } + + computeTree(nodes, parents, env) { + return component.computeTree(this.handle, nodes, parents, env); + } + + release() { + if (this.handle != null) { + component.release(this.handle); + this.handle = null; + } + } + + [Symbol.dispose]() { + this.release(); + } + + [Symbol.asyncDispose]() { + this.release(); + return Promise.resolve(); + } +} From 8a3bc50f9da4ecd1abb04a92a142888aa54edb98 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:26:45 -0400 Subject: [PATCH 5/9] Fold prepare into compile: compile(css) returns a disposable Sheet --- experiments/css/smoke.mjs | 64 +++++++++++--------------- experiments/css/smoke.using.mjs | 19 ++++---- experiments/css/src/index.js | 79 +++++++++++++++++++++++++-------- 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index 38adc80..3c90958 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -2,7 +2,7 @@ // the embedded artifact. // Usage: node smoke.mjs -import { component, compile, prepare, compute, computeTree, release, decodeIr, NO_PARENT } from './src/index.js'; +import { component, compile, compute, computeTree, release, decodeIr, NO_PARENT } from './src/index.js'; const css = ` :root { --brand: oklch(70% .2 40); color: var(--brand) } @@ -16,15 +16,13 @@ const css = ` } `; -const { ir, diagnostics } = compile(css, 2); -console.log('diagnostics:', diagnostics.length ? diagnostics : 'none'); -if (!ir) { - throw new Error('compile returned no IR'); -} -console.log('ir bytes:', ir.byteLength); -const sheet = prepare(ir); +// compile both compiles and prepares: the Sheet carries diagnostics, the IR +// bytes, and the decoded rules. +const sheet = compile(css, 2); +console.log('diagnostics:', sheet.diagnostics.length ? sheet.diagnostics : 'none'); +console.log('ir bytes:', sheet.ir.byteLength); -const parsed = decodeIr(ir); +const parsed = decodeIr(sheet); console.log('ir version:', parsed.version); for (const rule of parsed.rules) { console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); @@ -66,9 +64,8 @@ if (JSON.stringify(perNode) !== JSON.stringify(whole)) { throw new Error('module-level computeTree disagrees with compute'); } -// Re-prepare + release exercises the slot lifecycle before the loop. -const other = compile('.x { color: blue }', 2); -const otherSheet = prepare(other.ir); +// Re-compile + release exercises the slot lifecycle before the loop. +const otherSheet = compile('.x { color: blue }', 2); otherSheet.compute(tree, parents, 2, env); otherSheet.release(); for (let i = 0; i < 500; i++) { @@ -92,21 +89,14 @@ if (growth > 0) { // pointer from compute-tree's lowering, so the option discriminant's padding // bytes are no longer zero. Reading the disc as anything wider than one byte // misreads these results as absent. -const hot = compile('.card { color: #fff }', 2); -if (hot.ir === null) { - throw new Error('compile after compute-tree reported no IR'); -} -const hotSheet = prepare(hot.ir); +const hotSheet = compile('.card { color: #fff }', 2); +hotSheet.compute(tree, parents, 1, env); hotSheet.release(); -// The rest of the poison matrix: re-preparing the ORIGINAL IR bytes… -const reSheet = prepare(ir); +// The rest of the poison matrix: re-compiling the ORIGINAL css… +const reSheet = compile(css, 2); // …and computing on a sheet adopted after compute-tree. -const adopted = compile('.card { color: #0f0 }', 2); -if (adopted.ir === null) { - throw new Error('compile after compute-tree reported no IR'); -} -const adoptedSheet = prepare(adopted.ir); +const adoptedSheet = compile('.card { color: #0f0 }', 2); const afterPoison = adoptedSheet.computeTree(tree, parents, env); if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); @@ -114,11 +104,23 @@ if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { reSheet.release(); adoptedSheet.release(); +// Diagnostics surface through the API: an invalid color warns and the rest +// of the rule recovers — the compile still succeeds. +const warned = compile('.card { color: notacolor; padding: 4px }', 2); +if ( + !warned.diagnostics.some( + (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), + ) +) { + throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); +} +warned.release(); + // Disposable protocol: release is synchronous, so `using` works via // Symbol.dispose and `await using` via Symbol.asyncDispose. (The `using` // syntax itself is exercised in smoke.using.mjs on Node 24+/bun.) { - const scoped = prepare(compile('.card { color: red }', 2).ir); + const scoped = compile('.card { color: red }', 2); if (scoped.compute(tree, parents, 1, env).length === 0) { throw new Error('scoped sheet returned no styles'); } @@ -128,17 +130,5 @@ adoptedSheet.release(); await scoped[Symbol.asyncDispose](); } -// Diagnostics surface through the ABI: an invalid color warns and the rest -// of the rule recovers. -const warned = compile('.card { color: notacolor; padding: 4px }', 2); -if ( - warned.ir === null || - !warned.diagnostics.some( - (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), - ) -) { - throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); -} - sheet.release(); console.log('smoke ok'); diff --git a/experiments/css/smoke.using.mjs b/experiments/css/smoke.using.mjs index 5809558..f7bb2f2 100644 --- a/experiments/css/smoke.using.mjs +++ b/experiments/css/smoke.using.mjs @@ -4,32 +4,35 @@ // Usage: node smoke.using.mjs import assert from 'node:assert/strict'; -import { component, compile, prepare, NO_PARENT } from './src/index.js'; +import { component, compile, decodeIr } from './src/index.js'; const tree = [ { tag: 'root', classes: ['root'], width: 400, height: 100, state: 0 }, { tag: 'card', classes: ['card'] }, ]; -const parents = [NO_PARENT, 0]; +const parents = [0xffffffff, 0]; const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; -// Sync `using`: the sheet is released when the block exits. +// Sync `using`: compile prepares and returns the Sheet; released at block exit. { - using sheet = prepare(compile('.card { color: red }', 2).ir); + using sheet = compile('.card { color: red }'); + assert(sheet.diagnostics.length === 0); assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); + assert(sheet.rules.length > 0); + assert(decodeIr(sheet).version === 2); } const pagesAfterSync = component.raw.memory.buffer.byteLength; // `await using` also works — release is synchronous but the protocol accepts it. { - await using sheet = prepare(compile('.card { color: blue }', 2).ir); + await using sheet = compile('.card { color: blue }'); assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); } -// Neither sheet leaked: a second compile/prepare/compute round still works -// and memory has not grown beyond the sheets we hold open. +// Neither sheet leaked: a third round still works and memory has not grown +// beyond the sheets we hold open. { - using sheet = prepare(compile('.card { color: green }', 2).ir); + using sheet = compile('.card { color: green }'); assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); } assert(component.raw.memory.buffer.byteLength <= pagesAfterSync + 65536 * 4); diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js index 131ae40..65360cb 100644 --- a/experiments/css/src/index.js +++ b/experiments/css/src/index.js @@ -3,29 +3,39 @@ // - `compiled` is the freestanding wasm module (brotli+Z85 embedded; the // module is decoded eagerly at import and ready for synchronous // instantiation). Regenerate with `node scripts/bundle-wasm.mjs`. -// - `component` is the instantiated engine exposing the raw ABI: -// `compile`, `prepare`, `compute`, `computeTree`, `release`. -// - The ergonomic surface is top-level: `compile(css)` returns -// `{ ir, diagnostics }`; `prepare(ir)` returns a `Sheet` whose -// `compute`/`computeTree` methods carry the handle, and which releases -// itself via `Symbol.dispose` (usable with `using`) or `Symbol.asyncDispose` -// (`await using`), since release is synchronous. +// - `compile(css)` is the main entry: it compiles CSS text, prepares the +// sheet, and returns a disposable `Sheet` — `using sheet = compile(css)` +// releases it at scope exit (`await using` also works; release is +// synchronous). The Sheet carries `ir` bytes, `diagnostics`, and lazily +// decoded `version`/`rules`. +// - `compute(sheet, ...)` / `computeTree(sheet, ...)` / `release(sheet)` are +// free-function alternatives to the Sheet methods. +// - For hosts that ship their own artifact: `loadCssComponent(bytes)` / +// `loadCssComponentFromModule(module)` return the raw ABI object +// (`compile`/`prepare`/`compute`/`computeTree`/`release`/`raw`). // // The embedded artifact decodes via `node:zlib`, so it targets Node-like // runtimes; browser hosts load the raw module and use `loadCssComponent`. import { compiled } from "./wasm.js"; -import { loadCssComponentFromModule } from "./abi.js"; -export { NO_PARENT, loadCssComponent, loadCssComponentFromModule, decodeIr } from "./abi.js"; +import { decodeIr as decodeIrBytes, loadCssComponentFromModule } from "./abi.js"; +export { NO_PARENT, loadCssComponent, loadCssComponentFromModule } from "./abi.js"; export { compiled }; export const component = loadCssComponentFromModule(compiled); +/// Compile CSS text into a prepared, disposable Sheet. Throws `CompileError` +/// (with `diagnostics`) if compilation produced no IR; warnings-only +/// compiles still succeed with `sheet.diagnostics` populated. export function compile(css, origin = 2) { - return component.compile(css, origin); -} - -export function prepare(ir) { - return new Sheet(component.prepare(ir)); + const { ir, diagnostics } = component.compile(css, origin); + if (ir === null) { + throw new CompileError(diagnostics); + } + const handle = component.prepare(ir); + if (handle === null) { + throw new Error("prepare rejected the IR"); + } + return new Sheet(handle, ir, diagnostics); } export function compute(sheet, nodes, parents, target, env) { @@ -44,18 +54,43 @@ export function release(sheet) { } } +/// Decode the compiled stylesheet IR for inspection. Accepts a Sheet or raw +/// IR bytes. +export function decodeIr(sheet) { + return decodeIrBytes(sheet instanceof Sheet ? sheet.ir : sheet); +} + function handleOf(sheet) { return sheet instanceof Sheet ? sheet.handle : sheet; } +/// Thrown when compilation produces no IR. `diagnostics` holds the +/// severity/code/message list from the engine. +export class CompileError extends Error { + constructor(diagnostics) { + super("compile returned no IR"); + this.name = "CompileError"; + this.diagnostics = diagnostics; + } +} + /// A prepared stylesheet. Wraps the raw handle so calls don't repeat it, and -/// releases itself at scope exit: `using sheet = prepare(ir)`. +/// releases itself at scope exit: `using sheet = compile(css)`. export class Sheet { - constructor(handle) { - if (handle == null) { - throw new TypeError("prepare rejected the IR"); - } + constructor(handle, ir, diagnostics) { this.handle = handle; + this.ir = ir; + this.diagnostics = diagnostics; + } + + /// Lazily decoded IR: version, origin, rules, layerOrder, keyframes, + /// opaque, warnings. + get version() { + return this.#decodedIr().version; + } + + get rules() { + return this.#decodedIr().rules; } compute(nodes, parents, target, env) { @@ -81,4 +116,10 @@ export class Sheet { this.release(); return Promise.resolve(); } + + #decodedIr() { + return (this.#decoded ??= decodeIrBytes(this.ir)); + } + + #decoded = null; } From 4ccf79709a955c598d23f75bf4edf5359aa7273e Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:31:34 -0400 Subject: [PATCH 6/9] Add Engine: multi-sheet aggregation with lazy recompile and inline-style merge --- experiments/css/smoke.mjs | 72 ++++++++++++++- experiments/css/src/engine.js | 168 ++++++++++++++++++++++++++++++++++ experiments/css/src/index.js | 2 + 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 experiments/css/src/engine.js diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index 3c90958..ef4faa2 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -2,7 +2,7 @@ // the embedded artifact. // Usage: node smoke.mjs -import { component, compile, compute, computeTree, release, decodeIr, NO_PARENT } from './src/index.js'; +import { component, compile, compute, computeTree, release, decodeIr, createEngine, NO_PARENT } from './src/index.js'; const css = ` :root { --brand: oklch(70% .2 40); color: var(--brand) } @@ -131,4 +131,74 @@ warned.release(); } sheet.release(); + +// --------------------------------------------------------------------------- +// Engine: multi-sheet aggregation, lazy recompile, removal, @layer, inline. +// --------------------------------------------------------------------------- + +function colorOf(declarations) { + const hit = declarations.find(([name]) => name === 'color'); + return hit ? hit[1] : null; +} +function assertColor(actual, expected, label) { + if (actual !== expected) { + throw new Error(`${label}: expected color ${expected}, got ${actual}`); + } +} + +const cardOnly = [{ tag: 'card', classes: ['card'] }]; +const cardRoot = [NO_PARENT]; + +// Later sheets win at equal specificity (document order). Colors come back +// canonicalized (named → shortest hex), so assert canonical values. +const engine = createEngine(); +engine.addSheet('.card { color: #0f0 }'); +engine.addSheet('.card { color: #f00 }'); +assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'sheet order'); + +// Lazy recompile on edit; removal reverts to the prior cascade. +const whiteId = engine.addSheet('.card { color: #123456 }'); +assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#123456', 'added sheet'); +if (engine.removeSheet(whiteId) !== true) throw new Error('removeSheet failed'); +assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'after removeSheet'); + +// @layer across sheets: layers rank by first appearance (later wins), and +// re-referencing an earlier layer must NOT reorder it to the end — that's +// the discriminator the concatenation scheme has to preserve. +const layerEngine = createEngine(); +const bId = layerEngine.addSheet('@layer b { .card { color: #0f0 } }'); +const aId = layerEngine.addSheet('@layer a { .card { color: #ff0 } }'); +const plainId = layerEngine.addSheet('.card { color: #234567 }'); +assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#234567', 'unlayered wins'); +layerEngine.removeSheet(plainId); +assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#ff0', 'later layer wins'); +const bAgain = layerEngine.addSheet('@layer b { .card { color: #00f } }'); +assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#ff0', '@layer first-appearance survives re-reference'); + +// Inline styles: inline-normal beats sheet-normal, sheet-!important beats +// inline-normal, inline-!important beats everything. Inline values pass +// through un-canonicalized. +const inlineEngine = createEngine(); +inlineEngine.addSheet('.card { color: #fff; background: black }'); +inlineEngine.addSheet('.card { color: #f00 !important }'); +const inlineNode = [{ tag: 'card', classes: ['card'], style: 'color: blue; border: none' }]; +const inlineImp = [{ tag: 'card', classes: ['card'], style: 'color: blue !important' }]; +assertColor(colorOf(inlineEngine.compute(inlineNode, cardRoot, 0, env)), '#f00', 'sheet !important beats inline'); +assertColor(colorOf(inlineEngine.compute(inlineImp, cardRoot, 0, env)), 'blue', 'inline !important wins'); +const noShield = createEngine(); +noShield.addSheet('.card { color: #fff }'); +assertColor(colorOf(noShield.compute(inlineNode, cardRoot, 0, env)), 'blue', 'inline beats sheet-normal'); +if (noShield.compute(inlineNode, cardRoot, 0, env).some(([n, v]) => n === 'border' && v !== 'none')) { + throw new Error('inline border not applied'); +} +noShield.release(); + +// Engine dispose releases the sheet; a later compute lazily re-prepares. +inlineEngine[Symbol.dispose](); +assertColor(colorOf(inlineEngine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'engine resurrects after dispose'); +inlineEngine.release(); +layerEngine.release(); +engine.release(); +console.log('engine ok'); + console.log('smoke ok'); diff --git a/experiments/css/src/engine.js b/experiments/css/src/engine.js new file mode 100644 index 0000000..b789777 --- /dev/null +++ b/experiments/css/src/engine.js @@ -0,0 +1,168 @@ +// Engine: an aggregating front for the wasm component, mirroring how a +// browser hosts stylesheets. +// +// - Sheets are kept as source text and concatenated in the order they were +// added. That is exactly the cascade browsers apply across multiple author +// stylesheets (later wins at equal weight; `@layer` first-appearance order +// composes across sheets), so semantics stay correct. The cost is a full +// recompile on edit, which is deferred (dirty flag) until the next compute +// call, so N addSheet calls in a row cost one compile. +// - Inline styles (`node.style`) are merged per node after the wasm cascade. +// The computed output crossing the ABI carries no importance flag, so the +// merge tracks the set of properties declared `!important` anywhere in the +// engine's sheets: inline-normal defers to those; inline-`!important` wins +// over everything. Two documented deviations from browser behavior: +// 1. an `!important` declaration in a rule that does NOT match the node +// still shields that property against inline-normal styles; +// 2. inline declarations do not seed inheritance — a child inherits the +// sheet value of `--x`, not the parent's inline override, because the +// merge runs after the wasm inheritance pass. +// The correct fix is Rust-side: add `style: option` to the WIT +// `node` record and let the cascade slot inline declarations into their +// proper layer (author origin, above any selector specificity, below +// author-`!important`). + +import { compile } from './index.js'; + +/// Parse an inline style attribute into [name, value, important] triples. +export function parseInlineStyle(text) { + const declarations = []; + for (const part of String(text).split(';')) { + const colon = part.indexOf(':'); + if (colon < 1) continue; + const name = part.slice(0, colon).trim().toLowerCase(); + let value = part.slice(colon + 1).trim(); + let important = false; + if (/!\s*important$/i.test(value)) { + important = true; + value = value.replace(/!\s*important$/i, '').trim(); + } + if (name && value) declarations.push([name, value, important]); + } + return declarations; +} + +function importantProperties(rules) { + const names = new Set(); + for (const rule of rules) { + for (const declaration of rule.declarations) { + if (declaration.important) names.add(declaration.name.toLowerCase()); + } + } + return names; +} + +function mergeInline(computed, styleText, shielded) { + if (!styleText) return computed; + const inline = parseInlineStyle(styleText); + if (inline.length === 0) return computed; + const merged = new Map(computed); + for (const [name, value, important] of inline) { + if (!important && shielded.has(name)) continue; // sheet !important holds + merged.set(name, value); + } + return [...merged]; +} + +export class Engine { + constructor(origin = 2) { + this.origin = origin; + this.#sheets = []; + this.#dirty = true; + this.#sheet = null; + this.#shielded = new Set(); + } + + /// Add a stylesheet (source text). Returns an id usable with removeSheet. + addSheet(css) { + const id = this.#nextId++; + this.#sheets.push({ id, css }); + this.#dirty = true; + return id; + } + + /// Remove a previously added sheet by id. Recompiles lazily. + removeSheet(id) { + const before = this.#sheets.length; + this.#sheets = this.#sheets.filter((sheet) => sheet.id !== id); + if (this.#sheets.length !== before) this.#dirty = true; + return this.#sheets.length !== before; + } + + /// Drop all sheets. + clear() { + if (this.#sheets.length === 0) return; + this.#sheets = []; + this.#dirty = true; + } + + get sheetCount() { + return this.#sheets.length; + } + + /// Diagnostics from the last compile (lazily triggered). + get diagnostics() { + this.#ensurePrepared(); + return this.#sheet ? this.#sheet.diagnostics : []; + } + + /// Computed style for one node, with inline styles merged. + compute(nodes, parents, target, env) { + this.#ensurePrepared(); + if (!this.#sheet) return []; + const computed = this.#sheet.compute(nodes, parents, target, env); + return mergeInline(computed, nodes[target]?.style, this.#shielded); + } + + /// Computed styles for every node in one pass, inline styles merged + /// per node. + computeTree(nodes, parents, env) { + this.#ensurePrepared(); + if (!this.#sheet) return nodes.map(() => []); + const perNode = this.#sheet.computeTree(nodes, parents, env); + return perNode.map((computed, i) => mergeInline(computed, nodes[i]?.style, this.#shielded)); + } + + release() { + if (this.#sheet) { + this.#sheet.release(); + this.#sheet = null; + } + this.#dirty = true; + } + + [Symbol.dispose]() { + this.release(); + } + + [Symbol.asyncDispose]() { + this.release(); + return Promise.resolve(); + } + + #ensurePrepared() { + if (!this.#dirty && this.#sheet !== null) return; + this.release(); + const css = this.#sheets.map((sheet) => sheet.css).join('\n'); + if (css.trim() === '') { + this.#shielded = new Set(); + this.#dirty = false; + return; + } + const sheet = compile(css, this.origin); + this.#shielded = importantProperties(sheet.rules); + this.#sheet = sheet; + this.#dirty = false; + } + + #sheets; + #nextId = 1; + #dirty; + #sheet; + #shielded; +} + +/// Create an engine hosting any number of author stylesheets. +export function createEngine(origin = 2) { + return new Engine(origin); +} diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js index 65360cb..6397a2a 100644 --- a/experiments/css/src/index.js +++ b/experiments/css/src/index.js @@ -123,3 +123,5 @@ export class Sheet { #decoded = null; } + +export { Engine, createEngine, parseInlineStyle } from './engine.js'; From 798d7208c0276fd4338b72cfe82e13c8364dff87 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 21:55:39 -0400 Subject: [PATCH 7/9] Cascade multiple sheets and inline styles natively (WIT 0.4.0); fix sort_by --- .../crates/css-conformance/tests/behavior.rs | 50 ++-- experiments/css/crates/css-core/src/lib.rs | 4 + .../css/crates/css-runtime/src/cascade.rs | 242 ++++++++++++++---- experiments/css/crates/css-runtime/src/lib.rs | 2 +- .../css/crates/css-runtime/tests/runtime.rs | 138 +++++++--- experiments/css/crates/css-wasm/src/lib.rs | 43 ++-- experiments/css/smoke.mjs | 19 +- experiments/css/src/abi.js | 148 +++++------ experiments/css/src/engine.js | 155 ++++------- experiments/css/src/index.js | 15 +- experiments/css/src/wasm.js | 2 +- experiments/css/wit/css.wit | 14 +- 12 files changed, 515 insertions(+), 317 deletions(-) diff --git a/experiments/css/crates/css-conformance/tests/behavior.rs b/experiments/css/crates/css-conformance/tests/behavior.rs index 47e0f16..efd58af 100644 --- a/experiments/css/crates/css-conformance/tests/behavior.rs +++ b/experiments/css/crates/css-conformance/tests/behavior.rs @@ -75,11 +75,11 @@ fn nesting_case_styles_the_tree() { vec![NO_PARENT, 0, 0], ); let env = Environment::default(); - let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s, "font-weight"), "700"); // `color` inherits from .card; `.card .body` does not apply to the title. assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); - let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); // lightningcss canonicalizes the static color-mix() to a resolved oklab. let color = get(&s, "color"); assert!( @@ -104,14 +104,14 @@ fn level4_selectors_match() { ); let env = Environment::default(); // .row:has(> .col) matches the row. - let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); assert_eq!(get(&s, "outline"), "1px"); // nth-child(odd of .tagged): node 2 is the 1st tagged child → matches. - let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); assert_eq!(get(&s, "color"), "#ff0"); // node 3 is the 3rd child → nth-child(-n+3) applies (blue outranks it on // `color` specificity, so the corpus rule uses a distinct property). - let s = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 3, &env).unwrap(); assert_eq!(get(&s, "text-indent"), "4px"); } @@ -129,7 +129,7 @@ fn media_ranges_and_or_lists() { // all match at 600px; later source order wins. assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "purple" @@ -140,7 +140,7 @@ fn media_ranges_and_or_lists() { let interval = interval.sheet(); assert_eq!( get( - &compute(std::slice::from_ref(&interval), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&interval), &t, 0, &env).unwrap(), "color" ), "red" @@ -150,7 +150,7 @@ fn media_ranges_and_or_lists() { ..Default::default() }; assert!( - compute(std::slice::from_ref(&interval), &t, 0, &small) + compute(std::slice::from_ref(&&interval), &t, 0, &small) .unwrap() .get("color") .is_none() @@ -160,7 +160,7 @@ fn media_ranges_and_or_lists() { ..Default::default() }; assert_eq!( - get(&compute(&[interval], &t, 0, &edge).unwrap(), "color"), + get(&compute(&[&interval], &t, 0, &edge).unwrap(), "color"), "red" ); @@ -172,7 +172,7 @@ fn media_ranges_and_or_lists() { }; assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &small_height).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &small_height).unwrap(), "color" ), "purple" @@ -192,7 +192,7 @@ fn dark_scheme_reaches_the_runtime() { // and appears later in source order. assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "orange" @@ -203,7 +203,7 @@ fn dark_scheme_reaches_the_runtime() { let dark = dark.sheet(); assert_eq!( get( - &compute(std::slice::from_ref(&dark), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&dark), &t, 0, &env).unwrap(), "color" ), "green" @@ -227,7 +227,7 @@ fn container_corpus_case() { vec![NO_PARENT, 0], ); let env = Environment::default(); - let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); // inline-size 800 > 45em (720px) → red; width 800 > 200 → the anonymous // container query would win too but it targets `(width < 200px)` → no. assert_eq!(get(&s, "color"), "red"); @@ -247,12 +247,12 @@ fn var_case_resolves_through_the_tree() { vec![NO_PARENT, 0, 0, 0], ); let env = Environment::default(); - let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); assert_eq!(get(&s, "margin"), "8px"); - let s = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); assert_eq!(get(&s, "padding"), "2px"); - let s = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 3, &env).unwrap(); assert_eq!(get(&s, "color"), "oklch(70% .2 40)"); } @@ -265,7 +265,7 @@ fn layer_order_matches_first_appearance() { // Unlayered rule beats both layered rules. assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "#00f" @@ -280,7 +280,7 @@ fn important_beats_source_order() { let env = Environment::default(); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "red" @@ -297,10 +297,10 @@ fn hex_colors_reach_computed_styles() { ); let env = Environment::default(); // 3-digit hex on `color`… - let a = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + let a = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); assert_eq!(get(&a, "color"), "#fff"); // …and on `border-color`. - let f = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let f = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&f, "border-color"), "#333"); } @@ -314,12 +314,12 @@ fn single_line_rules_reach_computed_styles() { ); let env = Environment::default(); // Both declarations of the single-line rule survive. - let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "#fff"); assert_eq!(get(&s, "padding"), "4px"); // The nested rule opened and closed on the same line as its parent. // (`blue` canonicalizes to `#00f`: equal-length ties go to hex.) - let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s, "color"), "#00f"); } @@ -343,7 +343,7 @@ fn compile_and_prepare_survive_compute_tree() { vec![NO_PARENT, NO_PARENT], ); - bombshell_css_runtime::cascade::compute_tree(std::slice::from_ref(&sheet), &t, &env, &[]); + bombshell_css_runtime::cascade::compute_tree(std::slice::from_ref(&&sheet), &t, &env, &[]); // A later compile walks the same pipeline: identical source, identical IR. let second = load(&read_case("colors/02-hex.css")); @@ -353,8 +353,8 @@ fn compile_and_prepare_survive_compute_tree() { // and computes. let decoded = decode_ir(&encode_ir(&first.ir)).expect("re-encoded IR decodes"); let reloaded = Stylesheet::from_ir(&decoded, 0); - let s = compute(std::slice::from_ref(&reloaded), &t, 0, &env).unwrap(); + let s = compute(std::slice::from_ref(&&reloaded), &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "#fff"); - let f = compute(std::slice::from_ref(&reloaded), &t, 1, &env).unwrap(); + let f = compute(std::slice::from_ref(&&reloaded), &t, 1, &env).unwrap(); assert_eq!(get(&f, "border-color"), "#333"); } diff --git a/experiments/css/crates/css-core/src/lib.rs b/experiments/css/crates/css-core/src/lib.rs index ce4dadc..1b8f15f 100644 --- a/experiments/css/crates/css-core/src/lib.rs +++ b/experiments/css/crates/css-core/src/lib.rs @@ -99,6 +99,10 @@ pub struct NodeSnapshot { pub width: Option, /// Layout height in CSS px, if known. pub height: Option, + /// Inline style declarations as authored (e.g. `color: red; --x: 1`). + /// Parsed by the runtime cascade; author origin, above every stylesheet + /// declaration of the same importance, below author-`!important` rules. + pub style: Option, } /// A flat tree snapshot: nodes in document order plus each node's parent index. diff --git a/experiments/css/crates/css-runtime/src/cascade.rs b/experiments/css/crates/css-runtime/src/cascade.rs index d159c03..f25b7a2 100644 --- a/experiments/css/crates/css-runtime/src/cascade.rs +++ b/experiments/css/crates/css-runtime/src/cascade.rs @@ -12,10 +12,11 @@ use crate::values::resolve_vars; use bombshell_css_core::{CompiledStylesheet, ContainerQuery, Declaration, Origin, TreeSnapshot}; use rustc_hash::FxHashMap as HashMap; -/// Property map for one element: keys are canonical IR property names; -/// values borrow the IR until a `var()` substitution or inheritance -/// materializes something new. -pub type StyleMap<'ir> = HashMap<&'ir str, Cow<'ir, str>>; +/// Property map for one element: keys are canonical IR property names +/// (borrowed) or inline-style names (owned); values borrow the IR until a +/// `var()` substitution, inheritance, or an inline declaration materializes +/// something new. +pub type StyleMap<'ir> = HashMap, Cow<'ir, str>>; /// Properties that inherit by default (CSS-wide custom properties also /// inherit unconditionally). @@ -223,15 +224,7 @@ impl<'ir> Stylesheet<'ir> { } fn layer_rank(&self, layer: Option<&[String]>) -> u32 { - match layer { - None => u32::MAX, - Some(path) => self - .layer_order - .iter() - .position(|p| p.as_slice() == path) - .map(|p| p as u32) - .unwrap_or(u32::MAX - 1), - } + layer_rank(self.layer_order, layer) } } @@ -241,8 +234,11 @@ impl<'ir> Stylesheet<'ir> { /// insertion sort costs nothing and behaves identically. fn sort_by(v: &mut [T], mut less: impl FnMut(&T, &T) -> bool) { for i in 1..v.len() { + // The element being inserted is `v[i]`; compare it against the + // sorted prefix. Comparing `v[j]` instead would test the wrong pair + // as soon as the key moves more than one slot. let mut j = i; - while j > 0 && less(&v[j], &v[j - 1]) { + while j > 0 && less(&v[i], &v[j - 1]) { j -= 1; } if j != i { @@ -255,20 +251,21 @@ fn sort_by(v: &mut [T], mut less: impl FnMut(&T, &T) -> bool) { /// values borrow until substitution produces something new. #[derive(Debug, Clone, Default, PartialEq)] pub struct ComputedStyle<'ir> { - /// (property name, computed value), sorted by name. - pub declarations: Vec<(&'ir str, Cow<'ir, str>)>, + /// (property name, computed value), sorted by name. Names borrow the IR + /// unless they came from an inline style. + pub declarations: Vec<(Cow<'ir, str>, Cow<'ir, str>)>, } impl<'ir> ComputedStyle<'ir> { pub fn get(&self, name: &str) -> Option<&str> { self.declarations .iter() - .find(|(n, _)| *n == name) + .find(|(n, _)| n.as_ref() == name) .map(|(_, v)| v.as_ref()) } pub(crate) fn from_map(map: StyleMap<'ir>) -> ComputedStyle<'ir> { - let mut declarations: Vec<(&'ir str, Cow<'ir, str>)> = map.into_iter().collect(); + let mut declarations: Vec<(Cow<'ir, str>, Cow<'ir, str>)> = map.into_iter().collect(); sort_by(&mut declarations, |a, b| a < b); ComputedStyle { declarations } } @@ -279,7 +276,7 @@ impl<'ir> ComputedStyle<'ir> { /// The full ancestor chain is cascaded so container lookups, inheritance, /// and style queries see computed values. pub fn compute<'ir>( - sheets: &[Stylesheet<'ir>], + sheets: &[&Stylesheet<'ir>], tree: &TreeSnapshot, target: usize, env: &bombshell_css_core::Environment, @@ -301,7 +298,7 @@ pub fn compute<'ir>( /// restricts which entries are resolved (an empty list resolves every /// node). Unresolved entries are empty styles. pub fn compute_tree<'ir>( - sheets: &[Stylesheet<'ir>], + sheets: &[&Stylesheet<'ir>], tree: &TreeSnapshot, env: &bombshell_css_core::Environment, targets: &[usize], @@ -321,6 +318,29 @@ pub fn compute_tree<'ir>( // Resolved maps per node, kept so descendants can inherit computed // values; the sorted `ComputedStyle` form is built only for output. let mut maps: Vec>> = Vec::with_capacity(count); + + // `@layer` order is a document-wide property: layers rank by first + // appearance across all sheets in cascade order. With a single sheet + // this is exactly that sheet's own order — no allocation, and the + // zero-allocation frame-loop claim holds for the common case. + let merged: Vec> = if sheets.len() == 1 { + Vec::new() + } else { + let mut merged: Vec> = Vec::new(); + for sheet in sheets { + for path in sheet.layer_order { + if !merged.iter().any(|existing| *existing == *path) { + merged.push(path.clone()); + } + } + } + merged + }; + let layers: &[Vec] = if sheets.len() == 1 { + sheets[0].layer_order + } else { + &merged + }; for index in 0..count { if !wanted[index] { maps.push(None); @@ -353,13 +373,13 @@ pub fn compute_tree<'ir>( current = tree.parent_of(ancestor); } - let mut map: StyleMap<'ir> = cascade(sheets, tree, index, env, &containers); + let mut map: StyleMap<'ir> = cascade(sheets, tree, index, env, &containers, layers); apply_css_wide_keywords(&mut map, &inherited); for (name, value) in &inherited { let inherits = - name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == *name); - if inherits && !map.contains_key(*name) { - map.insert(*name, value.clone()); + name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == name.as_ref()); + if inherits && !map.contains_key(name) { + map.insert(name.clone(), value.clone()); } } // var() substitution is part of computing the value: descendants @@ -374,6 +394,7 @@ pub fn compute_tree<'ir>( } fn computed_container_type(map: &StyleMap<'_>) -> Option { + // `map.get` accepts `&str` via `Borrow` on the `Cow` key. match map.get("container-type").map(|v| v.as_ref()) { Some("size") => Some(ContainerType::Size), Some("inline-size") => Some(ContainerType::InlineSize), @@ -385,12 +406,12 @@ fn computed_container_type(map: &StyleMap<'_>) -> Option { /// Resolve var() references in every non-custom declaration (custom /// properties keep their raw values until referenced). fn resolve_vars_in_map(map: &mut StyleMap<'_>) { - let names: Vec<&str> = map.keys().copied().collect(); + let names: Vec> = map.keys().cloned().collect(); for name in names { if name.starts_with("--") { continue; } - if let Some(value) = map.get(name) { + if let Some(value) = map.get(name.as_ref()) { if value.contains("var(") { match resolve_vars(value, map) { Some(resolved) => { @@ -398,7 +419,7 @@ fn resolve_vars_in_map(map: &mut StyleMap<'_>) { } None => { // Invalid at computed-value time. - map.remove(name); + map.remove(name.as_ref()); } } } @@ -408,37 +429,38 @@ fn resolve_vars_in_map(map: &mut StyleMap<'_>) { /// Handle `inherit`, `initial`, `unset`, and `revert` keywords. fn apply_css_wide_keywords<'ir>(map: &mut StyleMap<'ir>, inherited: &StyleMap<'ir>) { - let names: Vec<&str> = map.keys().copied().collect(); + let names: Vec> = map.keys().cloned().collect(); for name in names { - let keyword = map.get(name).map(|v| v.trim()).unwrap_or_default(); - let inherits = name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == name); + let keyword = map.get(name.as_ref()).map(|v| v.trim()).unwrap_or_default(); + let inherits = + name.starts_with("--") || INHERITED_PROPERTIES.iter().any(|p| *p == name.as_ref()); match keyword { - "inherit" => match inherited.get(name) { + "inherit" => match inherited.get(name.as_ref()) { Some(value) => { map.insert(name, value.clone()); } None => { - map.remove(name); + map.remove(name.as_ref()); } }, "unset" => { if inherits { - match inherited.get(name) { + match inherited.get(name.as_ref()) { Some(value) => { map.insert(name, value.clone()); } None => { - map.remove(name); + map.remove(name.as_ref()); } } } else { - map.remove(name); + map.remove(name.as_ref()); } } // No initial-value table yet: `initial` and `revert` drop the // declaration. "initial" | "revert" => { - map.remove(name); + map.remove(name.as_ref()); } _ => {} } @@ -447,20 +469,22 @@ fn apply_css_wide_keywords<'ir>(map: &mut StyleMap<'ir>, inherited: &StyleMap<'i /// Cascade one node: condition-filtered, selector-matched rules, sorted by /// origin/importance, layer, specificity, then source order; last write per -/// property wins. +/// property wins. Inline style declarations slot in at their cascade +/// boundaries (see [`parse_inline_style`]). fn cascade<'ir>( - sheets: &[Stylesheet<'ir>], + sheets: &[&Stylesheet<'ir>], tree: &TreeSnapshot, node_index: usize, env: &bombshell_css_core::Environment, containers: &[ContainerAncestor], + layers: &[Vec], ) -> StyleMap<'ir> { let Some(element) = crate::element::MatchElement::new(tree, node_index) else { return HashMap::default(); }; - struct Candidate<'a, 'ir> { - rule: &'a CascadeRule<'ir>, + struct Candidate<'a> { + declaration: &'a Declaration, origin_rank: u32, layer_rank: u32, specificity: u32, @@ -487,9 +511,9 @@ fn cascade<'ir>( } for declaration in rule.declarations { candidates.push(Candidate { - rule, + declaration, origin_rank: origin_importance_rank(sheet.origin, declaration.important), - layer_rank: sheet.layer_rank(rule.layer), + layer_rank: layer_rank(layers, rule.layer), specificity: rule.specificity, order: (sheet.sheet_index, rule.order), }); @@ -502,20 +526,142 @@ fn cascade<'ir>( < (b.origin_rank, b.layer_rank, b.specificity, b.order) }); + // Inline styles are author-origin declarations with precedence above + // every stylesheet declaration of the same importance: inline-normal + // slots in after all normal author rules, inline-`!important` after all + // author-`!important` rules. Both still lose to higher tiers (user / + // user-agent `!important`). Writing in ascending order means the last + // write per property wins. + let (inline_normal, inline_important) = tree.nodes[node_index] + .style + .as_deref() + .map(parse_inline_style) + .unwrap_or_default(); + // Ascending order: later candidates overwrite earlier ones, so the map // ends up holding the winning value per property. let mut winners: StyleMap<'ir> = HashMap::default(); - for candidate in candidates { - for declaration in candidate.rule.declarations { - winners.insert( - declaration.name.as_str(), - Cow::Borrowed(declaration.value.as_str()), - ); + let mut inline_normal_pending = !inline_normal.is_empty(); + let mut inline_important_pending = !inline_important.is_empty(); + for candidate in &candidates { + if inline_normal_pending && candidate.origin_rank >= 3 { + for (name, value) in &inline_normal { + winners.insert(Cow::Owned(name.clone()), Cow::Owned(value.clone())); + } + inline_normal_pending = false; + } + if inline_important_pending && candidate.origin_rank >= 4 { + for (name, value) in &inline_important { + winners.insert(Cow::Owned(name.clone()), Cow::Owned(value.clone())); + } + inline_important_pending = false; + } + let declaration = candidate.declaration; + winners.insert( + Cow::Borrowed(declaration.name.as_str()), + Cow::Borrowed(declaration.value.as_str()), + ); + } + if inline_normal_pending { + for (name, value) in &inline_normal { + winners.insert(Cow::Owned(name.clone()), Cow::Owned(value.clone())); + } + } + if inline_important_pending { + for (name, value) in &inline_important { + winners.insert(Cow::Owned(name.clone()), Cow::Owned(value.clone())); } } winners } +/// `@layer` order across sheets: layers rank by first appearance across all +/// sheets in the order the host supplies them (later layers win). Unlayered +/// ranks above every layer. +fn layer_rank(layers: &[Vec], layer: Option<&[String]>) -> u32 { + match layer { + None => u32::MAX, + Some(path) => layers + .iter() + .position(|p| p.as_slice() == path) + .map(|p| p as u32) + .unwrap_or(u32::MAX - 1), + } +} + +/// Parse an inline `style` attribute into (normal, important) declaration +/// lists, in authorship order. Splits on `;` and the first `:` outside +/// parens, brackets, and quotes, so values like `url(data:image/png;base64,…)` +/// survive. Names are lowercased; values pass through un-canonicalized (the +/// compiler never sees them). +fn parse_inline_style(style: &str) -> (Vec<(String, String)>, Vec<(String, String)>) { + let mut normal: Vec<(String, String)> = Vec::new(); + let mut important: Vec<(String, String)> = Vec::new(); + for part in top_level_split(style, ';') { + let Some(sep) = top_level_index(part, ':') else { + continue; + }; + let name = part[..sep].trim().to_ascii_lowercase(); + let value = part[sep + 1..].trim(); + let Some((value, is_important)) = strip_important(value) else { + continue; + }; + if name.is_empty() || value.is_empty() { + continue; + } + if is_important { + important.push((name, value.to_string())); + } else { + normal.push((name, value.to_string())); + } + } + (normal, important) +} + +/// Strip a trailing case-insensitive `!important`; returns `None` when the +/// declaration is empty after trimming. +fn strip_important(value: &str) -> Option<(&str, bool)> { + let lower = value.to_ascii_lowercase(); + if let Some(pos) = lower.rfind("!important") { + if lower[pos + "!important".len()..].trim().is_empty() { + let trimmed = value[..pos].trim_end(); + return Some((trimmed, true)); + } + } + (!value.is_empty()).then_some((value, false)) +} + +/// Split on every `sep` that sits outside parens, brackets, and quotes. +fn top_level_split(style: &str, sep: char) -> Vec<&str> { + let mut out: Vec<&str> = Vec::new(); + let mut depth = 0usize; + let mut quote: Option = None; + let mut start = 0usize; + for (index, ch) in style.char_indices() { + match ch { + '(' | '[' if quote.is_none() => depth += 1, + ')' | ']' if quote.is_none() => depth = depth.saturating_sub(1), + '\'' | '"' if quote.is_none() => quote = Some(ch), + c if Some(c) == quote => quote = None, + _ => {} + } + if ch == sep && quote.is_none() && depth == 0 { + out.push(&style[start..index]); + start = index + ch.len_utf8(); + } + } + out.push(&style[start..]); + out +} + +/// Index of the first `sep` outside parens, brackets, and quotes. +fn top_level_index(style: &str, sep: char) -> Option { + top_level_split(style, sep) + .first() + .map(|head| head.len()) + .filter(|&len| len < style.len()) +} + /// Cascade priority within (origin, importance), ascending. fn origin_importance_rank(origin: Origin, important: bool) -> u32 { match (origin, important) { diff --git a/experiments/css/crates/css-runtime/src/lib.rs b/experiments/css/crates/css-runtime/src/lib.rs index 9eb07de..cc1f9b5 100644 --- a/experiments/css/crates/css-runtime/src/lib.rs +++ b/experiments/css/crates/css-runtime/src/lib.rs @@ -29,7 +29,7 @@ pub use selectors::{SelectorList, parse_selector_list}; /// Resolve the computed style for `target`. pub fn compute<'ir>( - sheets: &[Stylesheet<'ir>], + sheets: &[&Stylesheet<'ir>], tree: &TreeSnapshot, target: usize, env: &Environment, diff --git a/experiments/css/crates/css-runtime/tests/runtime.rs b/experiments/css/crates/css-runtime/tests/runtime.rs index 50f5b0e..1b94a62 100644 --- a/experiments/css/crates/css-runtime/tests/runtime.rs +++ b/experiments/css/crates/css-runtime/tests/runtime.rs @@ -44,6 +44,86 @@ fn get<'a>(style: &'a bombshell_css_runtime::ComputedStyle, name: &str) -> &'a s .unwrap_or_else(|| panic!("missing {name}: {:?}", style.declarations)) } +#[test] +fn inline_styles_cascade_above_selectors() { + // All cases bind first: the invariant `SelectorList` borrow unifies IR + // lifetimes across the sheets of a single `compute` call, so every + // `Case` must outlive every `Stylesheet`. + let case = stylesheet(".card { color: #fff; background: black }"); + let important_case = stylesheet(".card { color: red !important }"); + let other_case = stylesheet(".other { color: red !important }"); + let sheet = case.sheet(); + let important = important_case.sheet(); + let other = other_case.sheet(); + let env = Environment::default(); + + // Inline-normal beats every author stylesheet declaration. + let mut n = node("card"); + n.classes = vec!["card".into()]; + n.style = Some("color: blue; border: none".into()); + let t = tree(vec![n], vec![NO_PARENT]); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "blue"); + assert_eq!(get(&s, "background"), "black"); + assert_eq!(get(&s, "border"), "none"); + + // Author `!important` beats inline-normal… + let s = compute(&[&sheet, &important], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "red"); + // …even when the important rule is in an earlier sheet. + let s = compute(&[&important, &sheet], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "red"); + + // …and an `!important` in a rule that does NOT match the node must not + // shield the property: the inline style wins. + let s = compute(&[&other, &sheet], &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "blue"); + + // Inline `!important` beats author stylesheet `!important`. + let mut n_imp = node("card"); + n_imp.classes = vec!["card".into()]; + n_imp.style = Some("color: blue !important".into()); + let t_imp = tree(vec![n_imp], vec![NO_PARENT]); + let s = compute(&[&sheet, &important], &t_imp, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "blue"); +} + +#[test] +fn inline_styles_participate_in_inheritance() { + // Inline declarations are part of the node's declared values BEFORE + // var() substitution and inheritance, so descendants inherit the + // inline-overridden computed values. + let case = stylesheet(".child { color: var(--brand) }"); + let sheet = case.sheet(); + let env = Environment::default(); + let mut parent = node("root"); + parent.style = Some("--brand: red".into()); + let mut child = node("child"); + child.classes = vec!["child".into()]; + let t = tree(vec![parent, child], vec![NO_PARENT, 0]); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); + assert_eq!(get(&s, "color"), "red"); +} + +#[test] +fn inline_styles_reject_malformed_declarations() { + // url() with a data URI keeps its colons and semicolons intact; junk + // parts are dropped instead of poisoning the cascade. + let case = stylesheet(".card { color: #fff }"); + let sheet = case.sheet(); + let env = Environment::default(); + let mut n = node("card"); + n.classes = vec!["card".into()]; + n.style = Some("background: url(data:image/png;base64,AAA); ::bad; color".into()); + let t = tree(vec![n], vec![NO_PARENT]); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); + assert_eq!(get(&s, "color"), "#fff"); + assert_eq!( + get(&s, "background"), + "url(data:image/png;base64,AAA)" + ); +} + #[test] fn matches_selectors() { let css = r" @@ -82,7 +162,7 @@ fn matches_selectors() { assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "red" @@ -90,21 +170,21 @@ fn matches_selectors() { // .a.b wins over .panel (higher specificity). assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(), "color" ), "#ff0" ); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(), "opacity" ), ".5" ); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(), "text-transform" ), "uppercase" @@ -112,14 +192,14 @@ fn matches_selectors() { // [data-x^='pre'] also matched. assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(), "text-decoration" ), "underline" ); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(), "color" ), "#00f" @@ -180,7 +260,7 @@ fn matches_combinators_and_structural_pseudos() { let env = Environment::default(); // item 3: first-child; ancestor `.root:has(.special)` styles all items - let s3 = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + let s3 = compute(std::slice::from_ref(&&sheet), &t, 3, &env).unwrap(); assert_eq!(get(&s3, "color"), "#00f"); assert_eq!(get(&s3, "cursor"), "pointer"); assert_eq!(get(&s3, "text-align"), "center"); @@ -189,14 +269,14 @@ fn matches_combinators_and_structural_pseudos() { assert!(s3.get("font-weight").is_none()); // item 4: nth-child(2), has previous sibling → bold - let s4 = compute(std::slice::from_ref(&sheet), &t, 4, &env).unwrap(); + let s4 = compute(std::slice::from_ref(&&sheet), &t, 4, &env).unwrap(); assert_eq!(get(&s4, "color"), "green"); assert_eq!(get(&s4, "font-weight"), "700"); assert_eq!(get(&s4, "opacity"), ".5"); assert_eq!(get(&s4, "text-decoration"), "line-through"); // item 5: last-child; :not(.skip) applies - let s5 = compute(std::slice::from_ref(&sheet), &t, 5, &env).unwrap(); + let s5 = compute(std::slice::from_ref(&&sheet), &t, 5, &env).unwrap(); assert_eq!(get(&s5, "color"), "#ff0"); assert_eq!(get(&s5, "cursor"), "pointer"); } @@ -234,12 +314,12 @@ fn state_pseudos_match() { let env = Environment::default(); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(), "color" ), "#fff" ); - let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s2 = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); assert_eq!(get(&s2, "color"), "#000"); assert_eq!(get(&s2, "outline"), "1px"); // app has no focus; focus-within on ancestors of focused node — app is ancestor, but we only computed leaf nodes; focus-within on btn itself also true @@ -281,20 +361,20 @@ fn cascades_by_origin_layer_specificity_order() { let env = Environment::default(); // Later author rule wins over earlier. - let s = compute(&[author.clone(), author_later.clone()], &t, 0, &env).unwrap(); + let s = compute(&[&author, &author_later], &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "green"); assert_eq!(get(&s, "padding"), "1px"); // !important beats normal even from an earlier sheet. - let s = compute(&[author.clone(), author_important.clone()], &t, 0, &env).unwrap(); + let s = compute(&[&author, &author_important], &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "#00f"); // Unlayered beats layered. - let s = compute(&[author.clone(), layered.clone()], &t, 0, &env).unwrap(); + let s = compute(&[&author, &layered], &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "red"); // User !important beats author !important. - let s = compute(&[author_important.clone(), important_user.clone()], &t, 0, &env).unwrap(); + let s = compute(&[&author_important, &important_user], &t, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "#fff"); // Specificity: #id beats .class. @@ -309,7 +389,7 @@ fn cascades_by_origin_layer_specificity_order() { }], vec![NO_PARENT], ); - let s = compute(&[specific], &t2, 0, &env).unwrap(); + let s = compute(&[&specific], &t2, 0, &env).unwrap(); assert_eq!(get(&s, "color"), "purple"); } @@ -345,19 +425,19 @@ fn inherits_and_resolves_custom_properties() { ); let env = Environment::default(); - let s0 = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + let s0 = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); assert_eq!(get(&s0, "color"), "oklch(70% .2 40)"); assert_eq!(get(&s0, "--brand"), "oklch(70% .2 40)"); // color inherits from root; --gap overridden locally; border-color via var - let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s1 = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s1, "color"), "oklch(70% .2 40)"); assert_eq!(get(&s1, "border-color"), "oklch(70% .2 40)"); assert_eq!(get(&s1, "margin"), "4px"); assert_eq!(get(&s1, "--gap"), "4px"); // explicit inherit keyword pulls parent's border-color - let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s2 = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); assert_eq!(get(&s2, "border-color"), "oklch(70% .2 40)"); assert_eq!(get(&s2, "--gap"), "4px"); } @@ -394,21 +474,21 @@ fn var_fallbacks_and_cycles() { let env = Environment::default(); assert_eq!( get( - &compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(), + &compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(), "color" ), "red" ); // Cycle → declaration dropped. assert!( - compute(std::slice::from_ref(&sheet), &t, 1, &env) + compute(std::slice::from_ref(&&sheet), &t, 1, &env) .unwrap() .get("color") .is_none() ); // Missing without fallback → dropped. assert!( - compute(std::slice::from_ref(&sheet), &t, 2, &env) + compute(std::slice::from_ref(&&sheet), &t, 2, &env) .unwrap() .get("color") .is_none() @@ -461,7 +541,7 @@ fn evaluates_media_container_supports() { }; // Both the 400px rule and the dark-scheme rule match; the dark rule is // later in source order, so it wins. - let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s1 = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s1, "color"), "#fff"); // With a light scheme only the viewport rule matches. @@ -470,13 +550,13 @@ fn evaluates_media_container_supports() { viewport_height: Some(800.0), ..Default::default() }; - let s1 = compute(std::slice::from_ref(&sheet), &t, 1, &light).unwrap(); + let s1 = compute(std::slice::from_ref(&&sheet), &t, 1, &light).unwrap(); assert_eq!(get(&s1, "color"), "red"); - let s2 = compute(std::slice::from_ref(&sheet), &t, 2, &env).unwrap(); + let s2 = compute(std::slice::from_ref(&&sheet), &t, 2, &env).unwrap(); assert!(s2.get("color").is_none(), "no container ancestor yet"); - let s3 = compute(std::slice::from_ref(&sheet), &t, 3, &env).unwrap(); + let s3 = compute(std::slice::from_ref(&&sheet), &t, 3, &env).unwrap(); assert_eq!(get(&s3, "color"), "oklch(70% .2 40)"); } @@ -510,7 +590,7 @@ fn container_queries_evaluate_against_ancestors() { vec![NO_PARENT, 0], ); let env = Environment::default(); - let s = compute(std::slice::from_ref(&sheet), &t, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &env).unwrap(); assert_eq!(get(&s, "color"), "#00f"); assert!(s.get("font-weight").is_none()); @@ -531,7 +611,7 @@ fn container_queries_evaluate_against_ancestors() { ], vec![NO_PARENT, 0], ); - let s = compute(std::slice::from_ref(&sheet), &t2, 1, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t2, 1, &env).unwrap(); assert_eq!(get(&s, "color"), "red"); } @@ -561,7 +641,7 @@ fn dark_scheme_and_state_via_nesting() { color_scheme: bombshell_css_core::ColorScheme::Dark, ..Default::default() }; - let s = compute(std::slice::from_ref(&sheet), &t, 0, &env).unwrap(); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); // :hover and dark-mode rules both match; dark rule is later in source order. assert_eq!(get(&s, "color"), "#fff"); } diff --git a/experiments/css/crates/css-wasm/src/lib.rs b/experiments/css/crates/css-wasm/src/lib.rs index a0d5b75..23035df 100644 --- a/experiments/css/crates/css-wasm/src/lib.rs +++ b/experiments/css/crates/css-wasm/src/lib.rs @@ -69,7 +69,7 @@ mod guest { } fn compute( - sheet: u32, + sheets: Vec, nodes: Vec, parents: Vec, target: u32, @@ -85,15 +85,16 @@ mod guest { SHEETS.with(|cell| -> Vec<(String, String)> { let slots = cell.borrow(); - let Some(entry) = slots.get(sheet as usize).and_then(|slot| slot.as_ref()) else { - return Vec::new(); - }; - let style = bombshell_css_runtime::compute( - std::slice::from_ref(&entry.sheet), - &tree, - target, - &environment, - ); + let loaded: Vec<&bombshell_css_runtime::Stylesheet> = sheets + .iter() + .filter_map(|&sheet| { + slots + .get(sheet as usize) + .and_then(|slot| slot.as_ref()) + .map(|entry| &entry.sheet) + }) + .collect(); + let style = bombshell_css_runtime::compute(&loaded, &tree, target, &environment); // Names borrow the IR and values borrow until a var() // substitution; the canonical-ABI lowering needs owned text. style @@ -109,7 +110,7 @@ mod guest { } fn compute_tree( - sheet: u32, + sheets: Vec, nodes: Vec, parents: Vec, env: WitEnvironment, @@ -121,15 +122,16 @@ mod guest { SHEETS.with(|cell| -> Vec> { let slots = cell.borrow(); - let Some(entry) = slots.get(sheet as usize).and_then(|slot| slot.as_ref()) else { - return Vec::new(); - }; - bombshell_css_runtime::cascade::compute_tree( - std::slice::from_ref(&entry.sheet), - &tree, - &environment, - &[], - ) + let loaded: Vec<&bombshell_css_runtime::Stylesheet> = sheets + .iter() + .filter_map(|&sheet| { + slots + .get(sheet as usize) + .and_then(|slot| slot.as_ref()) + .map(|entry| &entry.sheet) + }) + .collect(); + bombshell_css_runtime::cascade::compute_tree(&loaded, &tree, &environment, &[]) .into_iter() .map(|style| { style @@ -210,6 +212,7 @@ mod guest { active: n.state.contains(NodeState::ACTIVE), width: n.width, height: n.height, + style: n.style, }) .collect(); Ok(TreeSnapshot { nodes, parents }) diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index ef4faa2..465b1f7 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -156,7 +156,7 @@ engine.addSheet('.card { color: #0f0 }'); engine.addSheet('.card { color: #f00 }'); assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'sheet order'); -// Lazy recompile on edit; removal reverts to the prior cascade. +// Each sheet compiles once; removal reverts to the prior cascade. const whiteId = engine.addSheet('.card { color: #123456 }'); assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#123456', 'added sheet'); if (engine.removeSheet(whiteId) !== true) throw new Error('removeSheet failed'); @@ -191,7 +191,24 @@ assertColor(colorOf(noShield.compute(inlineNode, cardRoot, 0, env)), 'blue', 'in if (noShield.compute(inlineNode, cardRoot, 0, env).some(([n, v]) => n === 'border' && v !== 'none')) { throw new Error('inline border not applied'); } +// An !important in a rule that does NOT match the node must not shield: +// the inline style still wins (impossible under the old JS-side merge). +const wrongShield = createEngine(); +wrongShield.addSheet('.other { color: #f00 !important }'); +wrongShield.addSheet('.card { color: #fff }'); +assertColor(colorOf(wrongShield.compute(inlineNode, cardRoot, 0, env)), 'blue', 'non-matching !important does not shield'); +// Inline declarations seed inheritance: the child inherits the parent's +// inline-overridden custom property. +const inheritEngine = createEngine(); +inheritEngine.addSheet('.child { color: var(--brand) }'); +const inheritTree = [ + { tag: 'root', style: '--brand: red' }, + { tag: 'child', classes: ['child'] }, +]; +assertColor(colorOf(inheritEngine.computeTree(inheritTree, [NO_PARENT, 0], env)[1]), 'red', 'inline custom property inherits'); noShield.release(); +wrongShield.release(); +inheritEngine.release(); // Engine dispose releases the sheet; a later compute lazily re-prepares. inlineEngine[Symbol.dispose](); diff --git a/experiments/css/src/abi.js b/experiments/css/src/abi.js index cc5873d..6314720 100644 --- a/experiments/css/src/abi.js +++ b/experiments/css/src/abi.js @@ -24,14 +24,14 @@ function createComponent(instance) { let memory = new Uint8Array(exports.memory.buffer); let view = new DataView(exports.memory.buffer); const realloc = exports.cabi_realloc; - const compileFn = exports['bombshell:css/stylesheet@0.3.0#compile']; - const computeFn = exports['bombshell:css/stylesheet@0.3.0#compute']; - const prepareFn = exports['bombshell:css/stylesheet@0.3.0#prepare']; - const releaseFn = exports['bombshell:css/stylesheet@0.3.0#release']; - const computeTreeFn = exports['bombshell:css/stylesheet@0.3.0#compute-tree']; - const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute-tree']; - const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compile']; - const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.3.0#compute']; + const compileFn = exports['bombshell:css/stylesheet@0.4.0#compile']; + const computeFn = exports['bombshell:css/stylesheet@0.4.0#compute']; + const prepareFn = exports['bombshell:css/stylesheet@0.4.0#prepare']; + const releaseFn = exports['bombshell:css/stylesheet@0.4.0#release']; + const computeTreeFn = exports['bombshell:css/stylesheet@0.4.0#compute-tree']; + const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute-tree']; + const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compile']; + const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute']; function refresh() { memory = new Uint8Array(exports.memory.buffer); view = new DataView(exports.memory.buffer); @@ -104,6 +104,62 @@ function createComponent(instance) { return { pointer, count: entries.length }; } + function u32ListPointer(values) { + if (values.length === 0) { + return { pointer: 0, count: 0 }; + } + const pointer = realloc(0, 0, 4, values.length * 4); + refresh(); + values.forEach((value, i) => { + view.setUint32(pointer + i * 4, value, true); + }); + return { pointer, count: values.length }; + } + + /// Serialize the flat node list for `compute`/`compute-tree`: + /// tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), + /// classes list (ptr@20, len@24), attributes list + /// (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), + /// height option (disc@48, f32@52), style option + /// (disc@56, ptr@60, len@64) — 68 bytes per node. + function serializeNodes(nodes) { + const stride = 68; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + const style = node.style ? stringPointer(node.style) : null; + dataView.setUint8(base + 56, style ? 1 : 0); + if (style) { + dataView.setUint32(base + 60, style.pointer, true); + dataView.setUint32(base + 64, style.length, true); + } + }); + return allocate(data); + } + function compile(css, origin = 2) { const bytes = encoder.encode(css); const cssPointer = allocate(bytes); @@ -148,36 +204,9 @@ function createComponent(instance) { /// Resolve computed styles for every node in one pass. Returns one /// declaration list per node, index-aligned with `nodes`. - function computeTree(sheet, nodes, parents, env) { - const stride = 56; - const data = new Uint8Array(nodes.length * stride); - const dataView = new DataView(data.buffer); - nodes.forEach((node, i) => { - const base = i * stride; - const tag = stringPointer(node.tag); - dataView.setUint32(base, tag.pointer, true); - dataView.setUint32(base + 4, tag.length, true); - const id = node.id ? stringPointer(node.id) : null; - dataView.setUint32(base + 8, id ? 1 : 0, true); - if (id) { - dataView.setUint32(base + 12, id.pointer, true); - dataView.setUint32(base + 16, id.length, true); - } - const classes = stringListPointer(node.classes ?? []); - dataView.setUint32(base + 20, classes.pointer, true); - dataView.setUint32(base + 24, classes.count, true); - const attributes = attributeListPointer(node.attributes ?? []); - dataView.setUint32(base + 28, attributes.pointer, true); - dataView.setUint32(base + 32, attributes.count, true); - dataView.setUint8(base + 36, node.state ?? 0); - const hasWidth = node.width !== undefined && node.width !== null; - const hasHeight = node.height !== undefined && node.height !== null; - dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); - dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); - dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); - dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); - }); - const nodesPointer = allocate(data); + function computeTree(sheets, nodes, parents, env) { + const handleList = u32ListPointer(sheets); + const nodesPointer = serializeNodes(nodes); const parentBytes = new Uint8Array(parents.length * 4); const parentView = new DataView(parentBytes.buffer); @@ -185,7 +214,8 @@ function createComponent(instance) { const parentPointer = allocate(parentBytes); const result = computeTreeFn( - sheet, + handleList.pointer, + handleList.count, nodesPointer, nodes.length, parentPointer, @@ -226,40 +256,9 @@ function createComponent(instance) { releaseFn(sheet); } - function compute(sheet, nodes, parents, target, env) { - // node: tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), - // classes list (ptr@20, len@24), attributes list - // (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), - // height option (disc@48, f32@52) — 56 bytes per node. - const stride = 56; - const data = new Uint8Array(nodes.length * stride); - const dataView = new DataView(data.buffer); - nodes.forEach((node, i) => { - const base = i * stride; - const tag = stringPointer(node.tag); - dataView.setUint32(base, tag.pointer, true); - dataView.setUint32(base + 4, tag.length, true); - const id = node.id ? stringPointer(node.id) : null; - dataView.setUint32(base + 8, id ? 1 : 0, true); - if (id) { - dataView.setUint32(base + 12, id.pointer, true); - dataView.setUint32(base + 16, id.length, true); - } - const classes = stringListPointer(node.classes ?? []); - dataView.setUint32(base + 20, classes.pointer, true); - dataView.setUint32(base + 24, classes.count, true); - const attributes = attributeListPointer(node.attributes ?? []); - dataView.setUint32(base + 28, attributes.pointer, true); - dataView.setUint32(base + 32, attributes.count, true); - dataView.setUint8(base + 36, node.state ?? 0); - const hasWidth = node.width !== undefined && node.width !== null; - const hasHeight = node.height !== undefined && node.height !== null; - dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); - dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); - dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); - dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); - }); - const nodesPointer = allocate(data); + function compute(sheets, nodes, parents, target, env) { + const handleList = u32ListPointer(sheets); + const nodesPointer = serializeNodes(nodes); const parentBytes = new Uint8Array(parents.length * 4); const parentView = new DataView(parentBytes.buffer); @@ -267,7 +266,8 @@ function createComponent(instance) { const parentPointer = allocate(parentBytes); const result = computeFn( - sheet, + handleList.pointer, + handleList.count, nodesPointer, nodes.length, parentPointer, diff --git a/experiments/css/src/engine.js b/experiments/css/src/engine.js index b789777..b7991fe 100644 --- a/experiments/css/src/engine.js +++ b/experiments/css/src/engine.js @@ -1,134 +1,82 @@ // Engine: an aggregating front for the wasm component, mirroring how a // browser hosts stylesheets. // -// - Sheets are kept as source text and concatenated in the order they were -// added. That is exactly the cascade browsers apply across multiple author -// stylesheets (later wins at equal weight; `@layer` first-appearance order -// composes across sheets), so semantics stay correct. The cost is a full -// recompile on edit, which is deferred (dirty flag) until the next compute -// call, so N addSheet calls in a row cost one compile. -// - Inline styles (`node.style`) are merged per node after the wasm cascade. -// The computed output crossing the ABI carries no importance flag, so the -// merge tracks the set of properties declared `!important` anywhere in the -// engine's sheets: inline-normal defers to those; inline-`!important` wins -// over everything. Two documented deviations from browser behavior: -// 1. an `!important` declaration in a rule that does NOT match the node -// still shields that property against inline-normal styles; -// 2. inline declarations do not seed inheritance — a child inherits the -// sheet value of `--x`, not the parent's inline override, because the -// merge runs after the wasm inheritance pass. -// The correct fix is Rust-side: add `style: option` to the WIT -// `node` record and let the cascade slot inline declarations into their -// proper layer (author origin, above any selector specificity, below -// author-`!important`). - -import { compile } from './index.js'; - -/// Parse an inline style attribute into [name, value, important] triples. -export function parseInlineStyle(text) { - const declarations = []; - for (const part of String(text).split(';')) { - const colon = part.indexOf(':'); - if (colon < 1) continue; - const name = part.slice(0, colon).trim().toLowerCase(); - let value = part.slice(colon + 1).trim(); - let important = false; - if (/!\s*important$/i.test(value)) { - important = true; - value = value.replace(/!\s*important$/i, '').trim(); - } - if (name && value) declarations.push([name, value, important]); - } - return declarations; -} - -function importantProperties(rules) { - const names = new Set(); - for (const rule of rules) { - for (const declaration of rule.declarations) { - if (declaration.important) names.add(declaration.name.toLowerCase()); - } - } - return names; -} +// Since `bombshell:css@0.4.0`, `compute`/`compute-tree` take a list of +// prepared sheets and cascade across them natively: later sheets win at +// equal weight, and `@layer` order is first-appearance across all sheets in +// the order given — the same rules a browser applies to document stylesheets. +// Each sheet compiles exactly once; adding or removing a sheet never +// recompiles the others. +// +// Inline styles are native too: `node.style` text is parsed by the runtime +// cascade (author origin, above every stylesheet declaration of the same +// importance, below author-`!important`), and inline declarations seed +// inheritance — children inherit the inline-overridden computed values. -function mergeInline(computed, styleText, shielded) { - if (!styleText) return computed; - const inline = parseInlineStyle(styleText); - if (inline.length === 0) return computed; - const merged = new Map(computed); - for (const [name, value, important] of inline) { - if (!important && shielded.has(name)) continue; // sheet !important holds - merged.set(name, value); - } - return [...merged]; -} +import { compile, component } from './index.js'; export class Engine { constructor(origin = 2) { this.origin = origin; this.#sheets = []; - this.#dirty = true; - this.#sheet = null; - this.#shielded = new Set(); } - /// Add a stylesheet (source text). Returns an id usable with removeSheet. + /// Compile and prepare a stylesheet, cascade-ordered after existing + /// sheets. Returns an id usable with removeSheet. Throws `CompileError` + /// (with `diagnostics`) when the sheet produces no IR. addSheet(css) { + const sheet = compile(css, this.origin); const id = this.#nextId++; - this.#sheets.push({ id, css }); - this.#dirty = true; + this.#sheets.push({ id, css, sheet }); return id; } - /// Remove a previously added sheet by id. Recompiles lazily. + /// Remove a previously added sheet by id. removeSheet(id) { - const before = this.#sheets.length; - this.#sheets = this.#sheets.filter((sheet) => sheet.id !== id); - if (this.#sheets.length !== before) this.#dirty = true; - return this.#sheets.length !== before; + const index = this.#sheets.findIndex((entry) => entry.id === id); + if (index === -1) return false; + this.#sheets[index].sheet.release(); + this.#sheets.splice(index, 1); + return true; } /// Drop all sheets. clear() { - if (this.#sheets.length === 0) return; + for (const entry of this.#sheets) entry.sheet.release(); this.#sheets = []; - this.#dirty = true; } get sheetCount() { return this.#sheets.length; } - /// Diagnostics from the last compile (lazily triggered). + /// Diagnostics from every sheet's compile, in cascade order. get diagnostics() { - this.#ensurePrepared(); - return this.#sheet ? this.#sheet.diagnostics : []; + return this.#sheets.flatMap((entry) => entry.sheet.diagnostics); } - /// Computed style for one node, with inline styles merged. + /// Computed style for one node across all sheets. compute(nodes, parents, target, env) { - this.#ensurePrepared(); - if (!this.#sheet) return []; - const computed = this.#sheet.compute(nodes, parents, target, env); - return mergeInline(computed, nodes[target]?.style, this.#shielded); + const handles = this.#handles(); + if (handles.length === 0) return []; + return component.compute(handles, nodes, parents, target, env); } - /// Computed styles for every node in one pass, inline styles merged - /// per node. + /// Computed styles for every node in one pass, index-aligned with + /// `nodes`. computeTree(nodes, parents, env) { - this.#ensurePrepared(); - if (!this.#sheet) return nodes.map(() => []); - const perNode = this.#sheet.computeTree(nodes, parents, env); - return perNode.map((computed, i) => mergeInline(computed, nodes[i]?.style, this.#shielded)); + const handles = this.#handles(); + if (handles.length === 0) return nodes.map(() => []); + return component.computeTree(handles, nodes, parents, env); } + /// Release every prepared sheet. A later `compute` re-prepares from the + /// retained source text. release() { - if (this.#sheet) { - this.#sheet.release(); - this.#sheet = null; + for (const entry of this.#sheets) { + entry.sheet.release(); + entry.sheet = null; } - this.#dirty = true; } [Symbol.dispose]() { @@ -140,26 +88,17 @@ export class Engine { return Promise.resolve(); } - #ensurePrepared() { - if (!this.#dirty && this.#sheet !== null) return; - this.release(); - const css = this.#sheets.map((sheet) => sheet.css).join('\n'); - if (css.trim() === '') { - this.#shielded = new Set(); - this.#dirty = false; - return; - } - const sheet = compile(css, this.origin); - this.#shielded = importantProperties(sheet.rules); - this.#sheet = sheet; - this.#dirty = false; + #handles() { + return this.#sheets.map((entry) => { + if (entry.sheet === null) { + entry.sheet = compile(entry.css, this.origin); + } + return entry.sheet.handle; + }); } #sheets; #nextId = 1; - #dirty; - #sheet; - #shielded; } /// Create an engine hosting any number of author stylesheets. diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js index 6397a2a..64f871f 100644 --- a/experiments/css/src/index.js +++ b/experiments/css/src/index.js @@ -1,4 +1,5 @@ -// @bomb.sh/css — bindings and runtime for the bombshell:css component. +// @bomb.sh/css — bindings and runtime for the bombshell:css component +// (WIT world `bombshell:css@0.4.0`). // // - `compiled` is the freestanding wasm module (brotli+Z85 embedded; the // module is decoded eagerly at import and ready for synchronous @@ -10,6 +11,8 @@ // decoded `version`/`rules`. // - `compute(sheet, ...)` / `computeTree(sheet, ...)` / `release(sheet)` are // free-function alternatives to the Sheet methods. +// - `createEngine()` hosts any number of sheets, cascaded natively by the +// component (see engine.js). // - For hosts that ship their own artifact: `loadCssComponent(bytes)` / // `loadCssComponentFromModule(module)` return the raw ABI object // (`compile`/`prepare`/`compute`/`computeTree`/`release`/`raw`). @@ -39,11 +42,11 @@ export function compile(css, origin = 2) { } export function compute(sheet, nodes, parents, target, env) { - return component.compute(handleOf(sheet), nodes, parents, target, env); + return component.compute([handleOf(sheet)], nodes, parents, target, env); } export function computeTree(sheet, nodes, parents, env) { - return component.computeTree(handleOf(sheet), nodes, parents, env); + return component.computeTree([handleOf(sheet)], nodes, parents, env); } export function release(sheet) { @@ -94,11 +97,11 @@ export class Sheet { } compute(nodes, parents, target, env) { - return component.compute(this.handle, nodes, parents, target, env); + return component.compute([this.handle], nodes, parents, target, env); } computeTree(nodes, parents, env) { - return component.computeTree(this.handle, nodes, parents, env); + return component.computeTree([this.handle], nodes, parents, env); } release() { @@ -124,4 +127,4 @@ export class Sheet { #decoded = null; } -export { Engine, createEngine, parseInlineStyle } from './engine.js'; +export { Engine, createEngine } from './engine.js'; diff --git a/experiments/css/src/wasm.js b/experiments/css/src/wasm.js index 960f593..4568bfb 100644 --- a/experiments/css/src/wasm.js +++ b/experiments/css/src/wasm.js @@ -4,5 +4,5 @@ const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; // the table lookups are non-null for well-formed z85 input; the bounds // checks below handle a short tail, so the assertions carry no risk function d(s,n){const b=new Uint8Array(n);let o=0;for(let i=0;ig-nnPfvLR2NT5@-*!6Xe=A1?f?:yr8}FQ1PHl%}wYb^08Q8*(kSFSK/zn-?eBqOhK#FiA!H1B.9XRICi$X/-(5hIdhi4q7P:1nvU/R4lfEV6=fTgEX2Bm^uIn^*e+N^aKtP=6R>GN}(FBMUs:)y1h9&3}IkqFLDt8W/1/tZWS&pBup%BDV5JcQf(GW.t96i]WT!>a9B+RHTUD#T1pUONSy$+^IOvwGcEIE-j/%Gzs{/F1#(p(ycAb>Xk=4-Ua(}tcBlW[%s@i6PPzE#}SX*q=q]U{[&lK^l+M.mSQUHZHn?MJ)lGV!O25H1Q:V#/Lpq6#WmXETE8g&G+@K5NA{OYI/KG3j&h[Gzam>IkW=YeaJ%Pu/ONI=@+{oUU!uIq3dn)(sZ^8GV@#+NB6)Jxf&7r]0A]$gnQJq)%?uX?*nCIw8s6O0/P5KxF}?qoT7qFq9)vK+MUHhfg0Qp+uAUDIRMn!DbfR!Fgr*>Rf7x{t9VO8}DqJVjEQtn%h[U[{Wf@jZ>W:pr-L6Bi!I3VBGiVGZC!tFJ^Xg4X#c=XT$v=2tI9DOO4sOL0+kq&7nkviS]ZXoyU]&F>kwj5sEpTZ2}Ms>EQD6ZJQELtIX{1(p9%M(z1fBXTG43HH9VfOc*n%GFL}7W.xZ)@3(*o@LgZoz!QAMN%8^=eq*wyWBgXFZ877nLse+RTeL>.-6P=HdWZFpoZM5pTDEA!^tsB3h/d<-hvEeN/Jo@BH9D45Lyj}=^6a{ugm4jt1]jgun!uXL-b&UGg!pqJ%9emC*B%rc*q1At-%9[yQ!+l-KE&[!rzgK#JMxTnFS.4wd(8-wh06?5PYm$QY^@t2pD*=)kC/76K@#V^^@%}}-2-]/kL*PL5/^bTq04qyUPP{SC}&D#EL9zIyW9<8S^x@$IE0{Ycb}7y@)9w{ul5>e@!uZKB1GTGL2W1fi@)8h$G#Rk}B)[Ztz=q1fGCkCXoXhupwyq}/ID9oZNkeF!L21h%7&V/FxJ7qG0V(=eBQc1ZL#gQ?yj-]Kjs(Z]6=hG0R4AfAu@tAIGek-N7&.k?c{G=f@t)@UAl3[7dL(d3KhT/WSnkKiRO9Wq{]WeZ6R5/=^K[bE]R}tyfJET{fqB:359M=EZyC5R(Oiw}C44F^2I-ruJN2YN-+Fik@u67OIoL0.Ii>H>0HZxwl-].H9cW@q6sTFr7*78OC9FJTe@qVW6VWHkWj@i*g>ffo=zJ5VC&ilsAw+NBg4h#6kmjvY+R[Cd]iXy6StF3J$<5U4910H=lS?&%BgCd5juHdj%b&dII6<3^Ju}yQStwO!xhpyhAgT6jHY0:!UpPDhW0h]f{GP(tuhhvI=ZYl(xmPvR&z8z-A5KUq*FJ:1h[So@Kx5r+}WCW}Tu00z^7Q.!]]ud>+2B-=7v{)tmUz%Px(i9ZxMzLDcg4si<3s2H=tcPm=w@W:^XM{MpIA8UGauVEu.}gRJi:]1k#A$hNus:Vhos>H4W5WIE9v$D)/6.g#DVU3xvQC(@}0eht5=LA7vFHc/)eB/$Fo9&LWMDLnC<*.KFFy}I?Qk85O0-yuRxnz5CTKP.p>:D!iduI3yuLbXO^ekN&%dYGq&[mW7Ju[+{3@2sdkMN056b4WjObEBrBp>eKlV/qBsq4egL0c+zAC8K4?t?pHdiTzJ{wFy9!XJkoa0x}GKG?$H+epDE@K^XTgCEVXu0m1bjFIY!1[Cv0B5r3mu(1j60)NyF6e9^5f4:CguGLsvAb?5P.65!eDBSJ2(PM<-(lorqImfp=x6f+Xf85{q14iDe$(2xr]9q=NQdc&&cbBsuWl>l/o^h1@@f%s[A7tey9M6TEnMnnzN%RY)YUBaLmPdhuFxIQfzge>XvKR}8K5Z&UI/uWN@vky8-?pOwO:=?2MvSgEcnEuZu8u5#S-4{J35v-on74vXSYcVn9}R!as%7Nq0KFZI>cH0bOiSU?@*N+E>7{2c(Eri2Y[])G6h8!x)hCR.yuY/QAx(PdId.}GBp^Spu+>!mOpnHWPsX=.?!>NViZ?Wrh]SKe{S]sEv>Wh=5g1Ly[#:Xov?GOa)R8$6BXjR*yCk#:hGOxN!BD51r.yCL0NTM^)+xZ#h1n@:n8E53+%H)4xjg-6/AI9F*yBO.aDBj@-11Qt@8/B3vdN%lIvL^D1T*K){2yUxpC1ndlysuE-o3Q1CE8/mJjX32c7@[oL^fUjlzs&#kSLeB26T72hg<&F+GNLXVLQKKchq(8gl9h6ZZ5?F2:/SQ.!3{>E3ooOr0k{MT42:cT!2!9(PONLt%bWBVD@{b*vLAMjNw[hcov47bdf:#/)au=i94uq{qlnQ<+?O(gq]MxFLikMqC@tL=@Jzpr^3smgoebxt?=vgz3qf8c(N{@a:WlYp}e/:5/R6F2z.V]eOtUgJfu]iG/OPc>@h=:B@C0q?SCE-mYxrCk+Tzbv!5Q!s71Rp:7!ntzTkOgHq6DeI]dPL8MK-T^Lmadc2b4zhTrT)Nw&:)KKLPIwa-A=&+C=n6]%Vzvh3=)R#]*DBb&:x9G3g+i4*!%k9&}H!ew#X}SW38c8j5uOLgvg/}]]9vih^kuGc*rc9MCYk(G<^f=u5bGJa3F*Z>Q)m>6EblM8q5yZ]cx&D*t-U2[.opUg&42hImwI8L*5jRZHgcQ(4j33nM}&8*&(D2k9UKon{H4?>E+REu/)HFhGNcfP{W+A+HJ5VV-^o80jBEM79/VX0q)!Uj^9V8m8Ya/Yp5[1cxCj<0wUqZZu2{Z6(?#=c!UhR>4Lzo?RE$2ilO.)AgDxrp5g-*O-Bhqp*v>a/c:u!Ta#SB&U8h^D+OKI<-$N5E8oiB4Bwvu9}7822nmG19VFf/bGJ:UJi$hXA.6fL5bZDw)<2VG0xP0]k#Z.*o+Q]qvpDC>1A[WyiFF#WT!}VvlNFD:m45=9!&lZ{hfK8pY5}F0Xr!^6/Rne+>CG3)nc:U^)z67@VG?hT{aj%g*-sKGT%j-aq[V@VIV)37-.}?.3.*4O(6/+Vrj=R@/T(rz3{S(]jRqLgrNUH{Wg!w)qlX/La$-TjKy9aJDVSy*Z>}ma}YaYGAbB]2d9IbaEd=gBUm>UH3}s[l>/sp@b55q(U94&(fCOzw)+Wn^KtpIWfPEl3O#lcWd#koFHGWnK?RIufJ=]ar>I%:1>ek4LdY8F@]W4^?S^HG8=HAGThsJDynnwL@{l6]Hq$9ukGjyfm6&JG5U+rI6Nu832*h}Q(}Dq!7%s2.tfk/Ogb&VFy.:gkiUK*bnm&^rkH!bS2m>TYjv^*B=nkl47DtDgM9%t?!4sNFe/z&d+XqK06rZ].}f2o}]p!6WYl1G*^@*Ks/ttXA2cr3N&-hrXdjG=DqzR2DDH7OD@iydt@1dMPC#>Y-*inisQWnL92(TMpj>fds^1JmP2Gi+Cr#aQJT}OzEmI8t+m7QC7I3RCh]sBh)iFn>*>ImG{5cDNCZoBj(QG2KKo0=%l7IDwj]k67UnzQI.8Xu1aD?53%EDaBsV[W3Uh9A@C0gm.G<[5W{8s}*pCrptq3cxGFvRgZi3>Hvk33n@O[3-)6*oN4:tv6/7@iJXKL6yk+<(4WnF(c*8J-A0??h&&NmX&gHIYqQVaqd$D0V{K>tPxdwrmX@F?s8&$q-(u:0XGHv+C2e[V.l4mqY#Bh!Xim4YS3M{u^@@j=)k->5c#bj5UTviq(261K?0o/ty1{:Q!%Ck/Sn^0cos11CyYue6ViGpHnpV.cC8?#i?nffx<(!3/UF1BA43DO.{ZXmnIDMoIv!=*8@oNT0-=E$cvlTx{0#YsR)/MkI1pSTEnN?:9bCgEmXVV5y*JkX{Pvi32cP3zJ=tQ[v{Bcbt>mpcIH2#ZeVm(/5Lce.PP&hxwi$I2kQ::Sl*BSj%S6>>$l-PLF&X3Dw^]}H!0Pwtp2Lo-IXLAGn6V{Kau8c]Y.hWo6OpKvHX((3Q=>{7=!i4@OgLk2/v*S!ab7UPU-WsBGvKAA)NthFy:3]I?Ta)W]s!-XokT=&Dir-K=.Pt6MzSH(c62V[8Tg!O?qvPo#HM(Tj&dNZ-v*L*HNXytLUd10TB$QsB7cB^oNIz=eBgl]w+DGdJw#fla7dKPZd.ZOSE?RHm5){M^O^w4F^B}SAOL=9xD?F/#utH-!d!qh?o!2aa1U9{a?/#OP:fmwZ:G+$tkyG(ZC?fHzLJb1*:@:[tgTpN+cQI5win7ef}C45RSs7dmq2B)zMsR:)t9$C=!k!Kmhu)MW2(R{^DXL.Pr8JtfC+A!]wMkVJEXwQWMOZd(q>@zG/+x:E-^7rtq/X#vX]@+3-mBbVyzFo9l))7BhW1aOO}oL&c@IG!m7i>/JV5.my10NjBw&IYH^x9ivhRWmAJR&&9vd8zH]#=[VoTva1Wr&sNz-XD7p*YpZq.QWzCVNTq#uQx=xw2}Lwr4Bx4%oeLKXDsc*#y:t+0.z=EwIptz#rxfkw8oRAi+1dU0YaN*tnjgv+4Vh{fEXxn*DRM?RZX*mv-LC7b@u4WsM>}a4Uf/U63%0&K+==>HU767yoy]N9(@&@JFZR$/pWK>P^(i5e4Rj+aiwKU^to7oM+WDbZtl]V8RViP#GU&<5J22SOCk}iPMs(&rhx6uj.w6XC.=izBfGZHF+tyP96I^>zoIh$u-PCo>+>}H7Pdv?0Y?nDuW/MJHFAprTB0Ly$$rU#iCGU&/26!XXvA94U4Lf4g5cPadY5ITTD>jZX.ZwT[.!OJ=yXUt+}rg}0u+l[{nZ&YR#ml3{7CosM=r7?O4+st/BlW^GN-BMDBv(7zEPzUn385f}tpMF&dh83!kdbBU.zxO6M-+A^BeOXuaZ:lx.[-1yad]LNG)-Z2-#>)8{/AkSxz>>:hs4hz[/:Ij#MAVt^JelUO>4sq1EH8l.qKBVGZ48+CZ}uL(>2YvQ9eoZ%dEHEjvyAMv)%:NL{sAlES?6&*po.geXa+.wl.M[0ruVo+Du{A#QeUhyCj.:Ho0a:m^@:67sPZq.:Jyyn*R!e{:of0=mQwr=^[BY7bXEgv/Uzey&)TcE>k?/e7H2h9%Pq{iI%.=C?JP8Q0M}7e2XMpi.nxgtq}zhVIrd(v@gpLlg49Q#}d8&$izCyFtD&RBnIF[tsOasF[%*OEn#UIr0mS1$!L{uEHn]09+^emj<5CTmB<6!7<:4X$m{rBe}TBpl:jtnJh]k&U%i6-mpBgb=6oAM7P#/8P4%/^y0Uu*UR.Cjm.7ISdrIaSX)p*p?=kP[Wb.p!n%1(49CW@U@HdmJWaCK07:J{Nd{q&n+yID[WaavQ[+(f]@{eF5K7Iz-%A]hz*HiVo2acsMKf!*CNa+Po<^C5aeV4a2f8[KRl0^o@8C&Uh/>(T]x^9/hKlc#4Nv!ySz6(P)p@xJgW$IP>#Rt^Fn#7rolvtT7sZJCMR0i[p}emfy#F5U/QR^LGOK)WLhf@6p+?B=wsOqs+jt82r{kpx94uVE&n/imN5>%X]U!kFvy%ZkNVctG^Bm%wCsQoJJJskP)fw/.jy%z:z?.IY)O><$r^M6#5c3l&E=XKFAXem7(58Qymc{f%-JE#Q2p4^w5%439LmCjKGF^]9mfQ:5vAbA0F1(7CRs%^+t*zmdNr(xaoe}0yV1kW}./?$U[V9^Xl-+]hBp>+$.Z%YxPq1Bo)(BLN*g2&YN@q$UbV!OkM(MW?G6ylA8%zMuq(ujXfv$Ufoi6UTWXL$.&tpz0R@XD.m$LG*&vkE!V}H0rt1rghc8]m9sfig3Poi?9oAK57G>Ml0DVIc/7X!db9*NH)ipN9$1=SQbhGvy:yOJb9/1gs3Cv92@c7Rk+&Vr0M8mQpbrLdGnYEv:F0BUuKr9!NjgRH8swfrfQWJ+FV?9UP/WKJm6Pch50S=r?PCk{icbCN0G)?4=ux$%(AWs$YOf/7z5ym[#^Fmvq+Vq-AWZL&m+U[$cHGlIGgdXwMH*{Q.?:6kCXLVGrY{x5S.3GU([yu&x9jjVZd7-^4Z(ce:3UbMUfLz2cqyPBVFxZt&m}Q)XwPH?%PX3.VZJwp6v8ADVm(#yn4.^J*.7FClm])n0Vr[Xw.^Jc-/C@j*&Z3&:Q%m?B??$Lf7O5G)!)mYDQCSs4B!&P7t%&m%.Tk=P0*0IsXpFTgyScV#=X5LUk-=x2Po9Tvr&F92dvM+y>j{wjW=FQM/&zZk<*?!%*o3Icow7nl0-t6IbjWgVf&pGe1c3TB*m&8yBs3iq:&oeib=nJOe(x<25p%Q{^})0FyXfdAJ1ny{n/gfEQoXNu#?<2kbzS)0fP2m^eYs(s?Md)h{k4@#pI$G$h/:2o4l@)xv/#YiLs{0=R.*3IaAvwoaUGBOZAs2mvU3eExzUL#!P-XY>{&X]:uNz&.7qDSS0l[[(r>o($(Gt4r*-=PcG@zLR3BX}j?SYeV1d>KvYcrb(t@!}5wl-Zf!p>29Z3}^ad{t=wc+Fxj*YA2!WSJZgXM1/U0C-s=YJv7Sfu&kx5^5XP/{F4HQoqIaMV8K-GWmITEV2p97+aiUcJnn+)Oi}Hm+q$+*cVUWpL2%I.eQwsasP2gGAQ>yOjHSr!C:+Y&dEEcJbhG>Juu[qr!gZGc@5dZE1Kf>YT9XOA-U+uMNznUILH960TIa-=MVJhtX}+8?>TMM1(Ti]Ur+=2%6K%JDSrS5.sR!D}tcsJWH)C>.UumAojsfpW-SX@KQj$:3x)=@/6CkdEMOwMW*B)#?]44uqaIb{Pc^Ho2UoRrph86g<(@Lgki6.ad8MXsUY7}51&CdHOY^LdevPp)O.FGUToeoHSm^]RDRD-qd[nLD?>P@a.Up]F/XUg7@e=BnF3g8[TWCS7pJY{W3RlUycjI)RPUSc]saj2kKpZm6FmOL9FnaiKqCX2ZxPUl*C{XiMK@j8!=S6AR:&0<@g&?yzlXkp{D)BnA4d3Z:I-xHWO9^(sB>@>grC$ykgC}K2+IUV[ZP(hqPxcqFxJVQZ11[C)r&TloeZh?Z>rSnhoWnv*Z?oJq+AC4G95#ve^%ki/TAMq5jURsw5e/p.mM>eWR}Lc:A+]LK?$[GO4a1TR)]L%!gdeTdDR?=3J:CLD?efZQEHbghOjqu+yJQSWk{>/v}PX/*Hb8Nl6+%hi*#y.:HsnH1spOQ%y1A#Z:Y5vRvqNPY)1vtqR7aw6^03zO*QNg)26a/<^-hJs1e@W{V#3#vv0.[>7[=MQ^N-6{7$Cv*+iP>6:g%c0&{Cta[pg>7/n/}!(<{2^jdAH4?P(5ca>(z(&YSs?x/{Y8j6#3x.fi(dC:G+:0O6%$go?EOOT5GOj7W4[U1mEhx#.)&)1%1F%dUlpltz$ypw]g10lXp=uk.94&2$X)*YJ*+A-}?Kj6.&7X.cSO3fg<4PihPFM[Cj?Uh4s*OW@^lTGY5T*cMzdfrpT>mv8lv7{9#@dtBixz/g*r0)6DL17nx8B[SOnxhBb]sz(w{tq+BY(2xru(x8tjd7nfJ&4.hiZF$=zjU.rQe:Y$7K(C.Z=E&eB>AIGJmQnvQa%96-:]x.Q#Ns8REA:C5sM{owGw+5h]7yx9XRlw4!pAr5pSo2T&dTT7aSkr0/30)+qfWPJcYJK0s[/o2AW8H-#HtQ5EfMz?+%>C[&EK{55(}sBxPRXrjOi$GmJ}+qn.oQ@tw*Zv88PSQw(8HgkHk!owl}k&dKlq?80[OCks{Nout5r{X=ALmXTz=ptDYyOFB5tIY1Oy#w77@BJ=tHLGT$>+w1=NonncA=Ew#J+-BX>FuBQgiv0^uequfuSC^yBMZaqfPMSa0[M5XeIm{8Uuu$kg:G(dB4vs.[@NB9*w/ogXvpjqe:dQ5Gjkl)z)Vgal8zP]5#qrpwu3LJ8fu(ff4w>Rhk4fBQG]oFUOL*oEQgHLI)J]8bIqM(SGc8mqEFV5VHks^2yEh!4]64c4o4uiAzz8!j$JfRhc<{iuC!VCNIgGIz-BK0+[rY.Xo3WR1+x=}pUAIb#1.x%&fQF6-Dylh+0I5(j%25c50tP:p-TlCZmwQ=TycMF*{(trMAN4vZ5*8ip&xe4cu5yftjiVw&>kRkVZae.3zmQ/fFV4Y4+!JHAz!X6)BexsV)i?Y/(rlAIcRx1u)cF}AmWD>XyiN#)[@y>fmz[$3q^W$9[crENzu2Lrnsg.XN=N{tx}rzLFj+3t39zp2$p^Y)2BI7xi&omZ+rgPu@9?h}[.i{2Pg6=H4(%G$OV7J7i!F8->zJ/j?gc@rVW65T@@>*t5A)SGD2ijxn]*RK4j*vMIdrsKcDwi)9DW:w#wTek!tbC3rn]7&FB)nHBVM%.:Ijb{C%qFO(?TCo8snoUO0>!VlBlDeS(ft+T:Uwb$&:{Y>KQONKyvop1rlKUpEp]zL8YPc6>AQmZ#Y1Bfleo(g5$b]1jEhNz[Q+:RCGN@9Y+R{BSeLm<=hamuV!%mxB7{mg7D(RIAFoJh2K/>0J$Xd{jFyLfMA#+N6EcA{OnRyeBKtknbx#j{[i<@fkxdzuQXFN-=$*(4/%Lc2{aacz1.A}poS<4?E9BDqg(Kv=k[<0OC?f+Q:kTV<-!z%d75XG#3g2Cye*TRL2Cr/DCAfA9%bRHlrodAUE[zlnkf.b3]XdtNR%CERQ=(x!=eKcuU5B+HE7a4dc[f8akejkU0CDGRQW[a8?Y=H{:MRmlHK*0/eOlmrbc-r)bW>hK-c&5!+>A>TAeq1p^I^m8&VjuJnV+C?^ImhD+Vy4>mx!3?P>7fPK9i2HxSZKK4Pt/R:@]y/Oan0f@lG7VnI*r8C>q]KzYB7KTXUul)HdCpXscuUoz@A+HM{Emr3x:[Y}0g?.ohQFS>l6r7yK!=t{EV1s&IlZO/4r>UQ4C-HB4b^-)uGU>77X}gD45#v{RUwT3&r:X8}]r!(=yD)IJkEXp=dxaJt@Ql<[Is7hLmI}r/P}C5Yy^(If2Uh$M5!I(5s:IB3Qy9*tg?)OEg5D%6H7oPp>xvDIBZO?h6.sXSbDx9cV3kY1u99)ZYBmdc=)=r+F5x3zhuZ+w>MH(KwkNCN@qS1&(yf0IW$AGx:Z5GY6:{Z>FTy:87:Ad1dl>U35vyhuQ>rv70RLjXNYCU1q{TzmJ}k/JE4lyE4AnptW}B+Zhl]?dj@?nOqg@DDN&@zF>r8cl]&a6m7x4L{E%<*Bt]N6AMbpLXzIKs5qy:7[$*dc>g36C@P>^k:UVQ-f?GQ3T%>Tzqew3O[*mG06r6v)=vH27!n?JJlQinw1umgi#{wvB83hLEQxb?}%*]+OegN7KgYJB&02feA1:RDUcbwWCUc=TOI9V%x2Z+C=NrZvJyz==3qA4c]SE(Rxn7v{jE/F21OVIaTR2xq.+L)EFYRIl?Yvpo8pTIGgW*Ox!Z-%mXVL5dsPe4oM>UE+nv^Y*ygbOmwOGV}t+eXHdH&/J+XCd=Av+.()vR?DOz*+fOy1?X!0/W2LGU.]:&42@+4ay{99gxcldp8l.cR]02uliqRC19gYV8aCjpyJl@+yxwFkbXfN#Cg}G2hWmP=t?PiN*m@.hoyQZZYtm.fG%EK9I%&JGs2eoF4qq?&8/WZ!yjUD9.!]fE+O#g}&LnXu21(3wHbz!}5K]hlf1?h-hxWVn#8-b5qv0(0dcil6jv$y2o00Tj3T5gu[v31XM2)(dhDK**(aXb@un}zgP@V{/Jbq4UQwE:pYgXay#@qK#s5[EvNH&Sh[]aL0*BgmYzK0jSvKp3crX$W3tz]B2DLNoeX4&(=hu^q#eD6X)TpsB}()tXC<KJhch388mq/8%2]HDwNDuR(V[&V+J{R%qbFzr=v6FEV3]v72B#+O<=crwbjbGQybiO-q(Cf[%FPoM:Gs>tB=mLUeB#)mjf}l?3CM(aIv/fXDxRPKd5xY>pZR:}rF5f8LKJp{$W&$EVz*2r:4d=sx@9+j/sQI&Aca^E%iD18tDKs>SQUC4{PLC&jl7L@HGRmxEON75jCC7T@3kSP3)8Rct.Vf0p:g-@K-E.67+k>hfnL&S>n@jPgS#cLvS3N2YGN>6lu{oh72Vvhx!c76B[{w%hV8p5v.O5FP3eLV{c5lYMv??$U8e2FY9^do^Q6iMeXG&B^}qCat#-Amg}8TBqrC-$iV.9A&E-hAA&Z6G9%18(j9G]3n]^/kY5ebUZ:YI47J.b+5gjbwdvP+tyZGT=Z>2&q1#ynre@gUm6Uodn]OD1c6%g=faoNnW%OFGgOOGHT$vY#Q!#n3CS$b%J4GoKxMubn$no>9Yi>.NHK4H4ZC?i1nbTOeJK3yASGJ}IQW2q9:@ehkZZgqG%u?}nNd@Snyyp:9*/ZYa6mXABc*>G.JRuzc&oBCdZbT?.ZH7Q@hl.wq14FbS)te&zkp0of^n>n!B@@W+DH4)u46@#Az=.D$9svv?2mlQb+s@${)pJz$I{!^6zMb6l1LB4ZZIR{5eTggs.meIv%mwso69X2u-vSFrlXb34}^rpSr4eNhx&t/KJ.zc?50MaAB86wTGgs*9ELa]&g:-=ak*%.%<3gPn{IJD&fh*QTKkd6C8enmTDQ={Ts#%+LE?Z[HC/bDnXA[7hziL[U2z3#uVfM{x8fR+Gb&yrPOf>$F)6Q$0/1gy*>jY2-0E.w!SIxg2MXN}M=$]PfKKTEGzrSD?V4*5ZNqnIPa=/r#:OrGr.fWz20Pdi(EzCiElYcElHMQiySV2m[@^3XnBKh1}0PxIXi>$3^PTEkokiyt7&NUhviPsW=c(OekHD4/V9>kuR@6]oO<}4l[9:%6Mw[x=go1^Pzcinrv$3EN1HQz5*F-kwaA*$3lmgc3>fMN3G}CwGdnQ6wa*]D2}zU!hWb<]=oYV{K^l49c{FGVjqx5Ed-f/T**kh/1VNtJIj{n7O4}V9U[S6Few^>N24w#Wl&binCm)%k%p$@V/>hTTbQOpx[zp(42*Hd4&({6)OjY0Mh*R77b2fI]#fYr^{s8m79/6W2vyfC{qg2RCe{LYq^@Zbc+^((DQ582[YDlNxGQxposhoGYJM?>eZs8u3609X.VZu*@7uoCiC*7Q(C9.>Qu#=mxB+bFy)uF?sxkIx0R/tawj)Yhn5jXF#}AelA)y5nd*As@2NbHrAiHr2SIy4b{mG-cZ/>F5^L9X4NQ!#fE:OJV{q07WWH>}}KxVTP#azP>Agu89*}pEg@z]j:r?*-v{kqEva5y}!8#bFfc)X9zY{u0ygMW=W)Wf%GE(NCYFELmMiz(=cvBb1JVp[w).}k-m6&mRO{#O<>.2noEa&D.:%@!M[)IM51F+k#Skmnx8V@7Cr?a79fX0dB!I{5GWyFr).7Vu6zGbH-u[8JsA{qL#HYEFL{vz@b(hzJUIuTBM4#L{!FNwvjy?e=HP.%^tuxD@6*Gi4O}W>J:Lf:*>nze!n@L1Jd&:6(9D!YP8Oc)p2Jl]qLTe4y)S1.suAT1vb2kew>WXR]4EOGKkaS{svo[=PWrnvz*xW4-615FY@qv})oqM{gg}O0uDCiU/]HtNv%M&s/CN36qdx(2Pb+aEx0JHYRml2{zZU}kZGOfuj{%C5T(J]3nimEomA+L[6w[de{LO-8Jg{lKVVnB@{3K-.QR)w-]!-Ny.ow@vOg&I7obf[L?SWl=Q50W1[aILUnXT9g]oF1#FA*ENF8r?^(P9Y&r5NsMERbl#js^!yZP.k-sp=zYKR2}lE8ihFj*c{8&vC+m1)9sz5kYjMu9cmz0LARBcPGdCW5&[Qi>4f9#[Uhlx).Z!cFCl7N>zhy.q?]OgL}Ufku!sfH@Fl$0Ho!RBRddvi#pr.c6}Nw-fxv3v/kqGZfUZ#X6Y>bRG%hTt!s&X)KyUH(FiPsBfYF/XmN.:ng{5PC:$:9r+NRw+?[bu}&D^VaOY2yT*b2so>R33L3MVbp8IZAT}dmYFzvKi>LO6%>A-)wAqD9r?rQtP.Jr8gtOX]rkfISVQqsAnTb1X^!6zc[e3ja3N[t$0A}UBqG1uP}huSZ*=dvTt4*^O6@RBD7@].2]BFb}Vs<}H$X0srZW{iKP@HUgVPMh{7<>oY^@.(as{i05a1ND72{N!4b4drK*H3f:xS6ap*yD-tWkm]Yv=Mfy8(N3a%F}vS4]L]9pVT>uCG[j>TroSr<$d2iR6:=q&(6vftQb?N6++&pK]P6l1M1S9=@um{6%iS$SF21qsVj]psVL^{v&wQyQD[:Cj67zE2(J!M+Nfhd7FIjV(U<+{ZtW($*l3AOhVvLkU&dIBN9D$2+TvTV{ZLo(CXw9rRQfF0XNOF3U5bfKAehb]IHU[V{ww{Q+oK/kFYg[=dxHjmsHY+EF%#lxuBN]AhA)]p-K%v<+7JMIv&yvP-s^MNU@ub5!W]Gz4k6N}>}=8t/1E{3vB+A2ieT@kTgNySaaA8WJNw)u4Ykj#k2KJ1ST!0.v]eJkL=k%6bEEvA5LO>+]>LEt2Uz=>kc65nyF7b)C:9)[E*MjaR(EBu]Y3.atE/TfzdJ/hOKjJF5@(ymx$4fA[vC/]S^tKwdt}OXR/8yrPj>P5V/8Ym5ArSB>q2LLmvMs5**{-NMPw5C}:je(P(Ta=F:qhnc-W]PrJhKhHt#M=8{aGe@Nv#=J.3#ssLg6jbfh+*})lTefCtOM0O7>1!^A/u{UIuiAr*g0?#[0zcz[(tB.q={MzS7YrmH0zQQm-lyRthY+$((J0Z.GT5Pb&cMBKv29#Y*3UiG=2GNUlIxCbA<<:^kf-=)9&sJMvprg}5LtR:vZes=*){{49oAn6F]cK[9=80T@3<{b}SQc0!&?{QRRW+jerIj9+gRB4rayqcKum@^yrniaT>ct^2?9/deTyE@KZIOkv9Io(S(5Nb3(>!b{bW45@g56*G6X$?stW+8EGDbBlwKE=#E6gd6iGp=w:D4:n.6@22m7wfHf)i1Cd#rg^*J?hr><)5W5Wa6OYeP<>[qf(-A)p-o2qgHX(/o>dGW}u3[+#)63$+B$mdvx/j+l{)Q}?ujfBbg$8i0=SDHk@$y1FAcW)71wCI]UNNo60-$bh]13BdIiywA2hHj4]O}jT%vu^tbitxi@d=LxBQkkl}Lr>n]CxZfae4hT$q5Le+<4+jNo^T2XadK2n1**7(*w>A-xky+JylPA0eC2r0L7kR)XzbyvS$nJuGMkd{$Bi.P8%L*g9>0[q.W#IH?V^aF=Ph+)RD=H1Gq9]1548FAu.}Nyq8x#z*>oyGhS?N46F5AKlVVMO*Zq:p>x(IkX&7fn1}X[LA4(KKtWup%8tCr[r%lq[-+6>{W]hork@pvWiA&uQ@h(pSeSQs!@.C7/Sa0e(XEnr1IXY(h8obtaILaZ<*Ubc@^YDP2qsTjhfu6Fi%D>o<8ZYW:(/rT{o1&#l1RoL&kq{cl$BIh6:n8Fx*Y&hA]0Q?}Fs=Wxa#8>rl09U&tUA9{raD#b1-S)=?9rOP&ehIMKw.kr?LSalyoU!P?ga+B%T+)Q)I)GhL0Y7]yF9C#).KwSu:Iy*m([w%tV@>y4hpp8te[mEyuP.rAJdjyuQj![}8EPz^fSjFnl6rjh{i@zNCh]H/SmKC7B8RG.s@mJ$GZfkptfSLtf/6h:.6HIYoH:RBhrU%1ti}5T>7z26o*nVy8:c0NJC[21?nK^bIT>QfI(]iYo6W(2&$-BNhtz6yWKNsm!aBg>OZ?2EbdT-=x+PB=c/&!zqW:Ewaz}4.{xR:?}+il}ImVDsGnC[2^{S3V!eQ)PRfzUmPs8:]kPXP22a4*yxH^3s]g!y6Kdhw+F{k)T7J@%h#f&.7d}QA?f)i2Z^Q-)p2n?trkNRH+khE9m)rmpRwesiBL1ceDJp:LsUDqDW20)Jza2u+GajTEPe>9r==c{hBkc03s0}SSlX?]:xGZ0Rm$=-6&T:EAO9LqWh**wL]EdA[VT{L#OUR=AB@lbF@3Ie/pRs97l+6S}w/3a(D+^ql2A2FdQvUyauDR*O787MjM9?#AGBJ?p*!}sO%SYDQ*Twlu+{Tk]>*?})5$%31c<+^0j]z>7aXq186A:0$#1@GxUH66XUsT(]s5vI#p{Pxs0oB6zbz$t}yY0tKW%cF>bT:lm$>?aM^Gc%O{@ksMKcs9&{--mxCATGyPCG*XmsD48%TM[N7&FxaV*&JnhxUfNTr&azY5f1QSoJ:]E]y)!(%fIkLkyj{wgaZ/JCS]!l4?Xycr%])drSO3ZHwwRc%kn6a%!b&+W0Y+!isww8E>3&Rkc][ZZ5GUrtq0#]p{tW@}s+3zwJrv)tgoK(a/R2AAtBZL8yyiuRhT7/so5nSD3X-{ljUgK?}9:&[]?Ex%oyecN1V(uyUI)aD)w!NUEOnH^(:XuU5BR!t-.(H/uUfBq(:O!Ru!EuF4RaS[Sr4AVx2(r%6n5!Tx62mA!lt>[0a2s(Owym6OL.8Q{pjI+4D%uV71$wQp&]sOqz{[f]}uY8J>hO=Hk(d8s8zHI#fDHg4Ro+Po&tV>>N74&m}whyk2I(y]rA7tPPi}kv]=a@>!Ot?WZxQeI>vMPOBb5fMo8#p](8Y}yOpLKgJ/*wGTiIva!IKd7U^zX=1H9+KtEc:G-3ClfuR(sJ?P$JaFaa9@c!@4Vsl2Yv&d.5vk6D8L-XR!bvITW0a}B5v<[J#:}cVg(>1/>S6GAK%k-ZsujMdSq#-NK^qLD{FO2{mye{s-$+1VpJ7vEf=BJ9$07Uaf^UStcU]-[Fu/Mmn{b[WiKpsxQSGg[#k/@4>gLb9/mSd8V%WZ.JLw(WrMaPEnKOlY0IPhm3FBx(]ZTo%!>sf(0y/B:Qb+V&U^8Eq9G^4wW-=QK3Uj.?0jCa:I*)u/ezA@f]KcpVQIs[b+Sew3k5ujQU(F1(zqM5]4}x5jVOg)N8TLR7ViVh)r<%ivpVh0VyL8umOtP>igcOx/H}lND^!F]+8^n6jKKLD6FL#G0].Wi-5N01:He/DE+MCYFdW/!Gh)B{KsG&s$t/R}+tweSZnwUNerDclQy:/-@WL06)5KoD+I!S94G*t3*^{eEe4r%5FMQ-qeSe{?+16fj#Q%lI]mbgZ)Vc]p8xu}FL8r0iBD8kZP5puiJK)(TEY&X5E4FYgsBQ@x99vpw/5.adOBuoc@A4tKhyYeHioPR0qu):RFf#5>&4[3[UfAznjdTV[@3.k6DlUmm=xtiGgzo1$1fQ6.7I)!)A{wa&*aJ#tT%fjz}P>rv.a{gX^zxW5hG2FHs0fGOy9L%)][M@e5lR3eFn.a9r*>7bN.&ELSUD$23d)RMyiR]l[%R]+Qnhn]}gR[?%uMB>UQgUL/&c(FHgOA$$$cnCV6DbQ?$*Hgf{HOlnmq<{x1>2QsZR[JJ9GU+8*Od82HcfSpTCMfsO8Z+p@LQY#}->Tfgr8?2I>c+[Z@Q&8p1]HoLC&40CPaDGfCuJLzGEbG]C+-WK-EXvf+mcPFNZFb-XSL(%I(L[oBU?veJ#{-1I7kvO*u0^UWl)l=&fG)r?l-Us)<$Ikp*[yBio8vSjq?sRskGTa.L1UH&D<-h=BCm*r}!>Ac#]i+4TQWX%0*CwYmr.I7h5nHhl:Rzw?Q#$kVK5t7+aQ9t=fb*9-vUNXOE0BeY3Wr@:pDj>u$ectq%MjQDQNKDQ&/)K/1t8595/S>*5Gex2hdwK{I/M}?jsmsQ7yYMR{FTl)dpk-W$-*u}q0zc/1Dpk:J>z>ZiIs7!5J!^l+rs.iysJO{359G7L2hrN=oYGMgCu.W]qqH!cqK&kl2V8>Y/db@[DkeTce{OAC[+JUOY3jlGsCm&@N2ag}Ep^-dmwWm(Z>Y0cYc}j=BWsXoFNpVfIV&&o25yXQ0e@9-FQ9cj!XJ:izRwzIiUlbR+5B#oF@P$dO?9Kk.WDu>sE7>q4Tsn3!bdQ#pkDwF)wNk1lT*t)y+-j@]pv9&{gf(Dzea6Kv>9SeK#A=Dm-@4l2*iblG53jPQ*+}3opb8[H)}^45Vcizwb*9d3lU}IpznxuxNhYGSvf@]g&!]RZ5e?KqwD%8#]C)[i!dV9zn@g-b=wWW<%lz@W-]=RL>LS#!x$Rl/FT+L:5??J:2+kaay0D/cF[[uCHD+gPg[Qk*yDj9=qh&5}1[cxU(hmXT%zmW-Ife1cnJjalBUzqReR3<%j!Z/M-.=l1!e0w<fIfn0gXT:vS(i=4Kz:LmH6{=#R5=58@8/ZmSaVK1F+]qqiaI)uuIh*t{47(tt^*kyJKh2*)FV1]5ee$1ou.5(^w>kA7B(HIg%b{&%%cZPj)9tdDXXYjqO[VQkN[]/J2-^^n4qlS]%WCE?BTB[P7^N/rj)+}sS8=rI+G8cMu>qG@-7iVHaY6OCZ^5!06)N]1#ArFSCz4)sewqb@A?/c}%7rhyB9rF*KJyseWAmkE{*y]QLdIAiEJkeg$jlqHp?OJ}o>VQz&kfksATrhKnz*LrW#N90^J]Nm^P&HDoxNjT28R}h%taE{LGnKSSALw>/PAh=Uaz3ki/CvQ9#RQ>u[Xo#*@yu-8An.lAAX#ORx?JF=4.nn-#iamB@}N^=PO6@Fje9D%L)SdN6a}BuV/b#fow#Wt(=>UN8171[W79UQ-iZZuvzUJCMJV{}edLqvg{Jh(K>UJ^/J$g42$exGY&NkQ[uKz+n7j//&2JY3TDY}04nu5(&$t39gygh0o9$W.ljy>D7:c-)7j$n-RgitVw@fi*dQ*8!wMRh7=knj.PF:Nvms#+zMSl&EmVEyh6&)3yqe.}P}tK3:Nx{2{8mPC*YpX{i/.}kB@1QraH3P/9C[[li19Ir-rVDY0^I(%9n0ze8Yp60v+@EN5o82*hL4]+2rTSV&eEB^*qZ=pHO*s^1qvZd:TagL5*hIA=EgHn8k7bC3^e8(5vzv^eT>:bhuu0JEIWKf?U$.stx0A?Rt4{o@{6^*]JT#o<-$}3?G(tl(PAp2tbkommjY+uoY.?(vLB]qnvTL4#+TnWVPXz%j^}ENksIp>S.K=:+DG-GmDK4c%h%LW9cy=!ia}keld}ljkhj@d2=2p=z)E+7}dUfA?!BT{}^B)3?FYDV0h@a@pVoB6y.+JQZ)*C8Z+/0q3P/ahmKLU]>jy7UCOi0Vo@gf/xZAWnz1i>w!?N})xe@Jh:tdtgu+K%dE<6>Pl*/U48umq(A=[7OtNK1sAo0Jr{haedg}XUxuj}}YvLK@(LxuBs)hkd8V:EB=mjlECdFvNpn1H(CmTspue.cdDsROC00]BHwjkSK3f6kH$D)iCxArH$z&+UG!HJ3/Y?BIFg?B3%k=FOcK%FJ/baTDrip8/u0^t-MgmI!Y#X=z[%iL]wC#y1{7!30Uds2AD:y9m]=2oh&gR}e-}0k%$5}Vo=w{xp!aQjF*fiUT7#{lx$K3jUej+BI{#R{?=LgFFk:.Lk3Nh[yaAMfa97VTEv{#@O3w!qyp*m:*@x5sJ3Bo(5JD%T:Gytw-&sDqclN7Jg4l>J@@Z%mzR1%OYgMOGJ$3Xu)tyMjogLe*M=z4rvYTajP?/):N=FcV7D^)!qvGHC/u3>pv&Q^Ox?2G{l3F=jt%e%P>U8*PF(A6}RNXG$ias[2$i@ru)HiucmMofu(P^>2rwO*QVRLBi=:/ZTWxc!OvBd]qVT4YEWsWgY0&P+xiBUsM:nGR71VE:41ba^Emi31+)M81dcgi%bBc{ShMY^E]qiCaa@0ZDd1}7TvzZi6Pq5Rx^ny^^]}]ogQz+Q7M66DSExOd?q24y{U!i*@+PfSaxgkC=^$jXQCb=0xP647?}.N:tR3ISzz1OOjh)]3}g?ldJc!$/zTG>VV+gwz7doeos.2x=>QnWO03EW2RZ}[d{2R^*M54i[vi!Ai.L0Y$>bUiR!9sx8^WuI>ZLwXC.?6lx61t)d.u.vM8}D]A[zHBW:h9%gvz8z1I:61hxQ:5py*%IH&(c4G=82JYf!=PTC+Meyr}>#/+DFH}Nb&z1xpQuOb/q6E({0^t3w@..zOF{ySl+-6D?Xlzm@LOQ?[u3w/k@e-VKBPtptaq>mp(E*NrrIfi6u=OzhGNe0j8FAo:GxVw3^kF@}GS&wD:yD96<)[z-(C?D[a&DAhx@vd{v}WLDc5?s!}EvfhZk)ABXQpa?]U{/Z6qR@M}hLS?^l00#Yn.Hw@v-n8IT$c5igR>z^J+#=HwN4Fe%JMjd]1H.Z%3BKTt>I03p$C[ol4mvd?vRQ!S*=8MJh=3?t#+a1h[K9<2zl?YK(5J1XKUm]$>Ls?c6GxbPYV{m#V$KSPjMc>>kQ[Ev[yB>90Ct-i@Er]#76*OzYxAe{1prSp-%Slo/y=O403P{DqJ*^tJ^#pA}.S^L/Mbb!wuCk!UVj9:2O%nGWXuaW@8zm!RyJcOcMkx!*AR}MTv9ovvgg1Wr:gc^#>cw}ZQ8L0M$MU#mpJpoYoMevBLzt9tX-kZv^A8R?s<.bG]LYP{d&c5IH8U{6D:Z>euTLtMuSYLJJ{s)7NvzRJ]Y2(gpe:!KHznw1+-xU[dCkT52l^/-tiKZSc(%6Mq(67DcTWBe0PC{V}BtDoL9lv<[HMOLzXF0reXs+yL{JJ!HAkPQ.ySri[gI5pqM1C9.WYLdWkvH^NIh)@cY5UQuj.^WfT*beoPQtb<2XmL/1f>QEoX0Xt}B@Ll=&}j3*N/i:uRgiJ&96S5LW#HXAg]$@09e0ddc)KJ<>=1.-I6#}WN)RJ]5KrcEX)VC@GbVIIgp]Gz+2AEbXOV00nFw6Ba/M(UYurD)H57rEP+{^5WS8}J<9a&*U]dq!X+M}iL-!xjGSYLMqO9.bxkm5k!@9mNCm1HvK-Kr9hEla3l6kc1GvE6^]8XpBw){WM{M?LlC#y(s[$ayaK7Edj]KL)(/%xfmBTWk@.*rhaG:hVLa<$oG)#Pu(?G<7$xbRMVd#B5Y7s3(]EMJtzl$!I+tX2&xgsoh3F^NSG)3{p7s*Ih+!zk]rtk/A$NiH]/6P^oj#Q3A+EDr1T+vNb6qG$jY}f8&E>MY*HTg0UKsCt9OjK{]8AJi&WVmF6&bg*kR2!gX8MxM8bNTsb7?!VRSNTxg:uixvkH!P>fh7dJgdra3$<8w^ou93]ydT]=q1cVjmSS4-+99yR7^u0xv0G5>^z*MzB=G?Lrbi@*:0q?z&:S@X!-EeeURSa^ovl-5t-L.IB*{Ab@hh%he+-)4lBhvV0(7wJO$VeUEp1f{DpH$T624iDCx@Oyh]Pi/LudvzFA1w{6^LczWFo$-@Pj/3ED]ZljmhNwbeah{6TH$wvfcGL$9u(:jU13q$FT4D8Yem]aCxj>KUE{8N6e8B]4hXH.3JNz:W0Ui#/Wbu7Mgcn@V:X:8Y.bKQu1{C$D!qVS:tQkL^K6{s2EoOhsX)Z4:?J4RnWfN2we8>3MRY@V5^32e-0L0/:}QJ%3V1}Mn]Fvvm2zb(6XbdG1T:P)Acwao7z1$oA5*b-g{3iSb.T>ko+XC[?Dk9X(U3wtB4$Yb5O6wL*vEU?+zfSByRdF^ycpa:Ya)Xq!$))MApB#U!&/Zmq7pM[uAY&=gpFng.-{1O41f0=uk1H)RP][z%jolmrzWO=C6U.}ntv:-)KpYO<4+1>ztErrM(niAvpIe1.zKaTcUYf.n&Va+Qlr509OIL5KmTNN1P6!L=hj*3HDpscP-hm/0N#{UjUQ?Ry<@/PJJ=g0WK*&sY$rn-%I2q4g5@VPWgSq{XfIcjr3SrRu))BM*tr$aN6g&lP4.v.8r4:JHUl%7}dL:N5xA/yFC.]xYst{@=Hb[!ES3@g5=A?d#y?5?T9z5HYj%w3!bZCbJPZ^q*kr#&OaROWM^DRjk3)YI&[iu+$YOWpC)&a?gqmhsMcUD*Mi&Z@Ka8:cqZ&VG47qY]PDn.{}Gj%mz?0Rt+Lh]t6R*>AQ(JZ4lGDn(5#z(M>HyCuBY2b{*{S]nVuB-n)?&z9NH]wO{Nl9V+8LD9ej7Ls+wQZudZ%^%amAEbNnUPC4dlbL.Kqezs#?Gi0:}LNqT)0)HT!ZBvyt.@6gti(N21HcYKIYieSB/r((>27y!s##pH9}cRn&I}fdAWEb{Icz3ecbM8Snr52d8z:AOK5@QRAbQOZM?UiJ*&xS>nvZC.LQn%kuwbc47Nl0pvmr}2Fs(NO?{a%14l*WbM&bJ8S#a$xcXz5W}$4ARQ:af-%vXqr0^V#gtHLJ7sX>yMKemqzQW)Utj$Uu6[$AmYyP)u1{fuj^}c3DjHXeGBc$j4Jp8.#7#($l+59usC=:FcK{@GzrP?w*0h0OmG{+eIg%gT{&wz.Wckg4[njUNg7Mai6vyA!8lMfLG@/?u0&v#Yg^zbS{n@I8iZDW3{5I3}#NX0@o!:Hrci+)^=yGHGVDGbz:{w&E?NI}16>BCh.Yce7eS+G(^q<$V2.^34MZ9(C=)%QU42D1I#O0IB%hq]l=i}+79>L)g&Qww.Jd8N1aUwEY26G@@{f>($Sv@IFk$5+cW}?O.#[H/.Y6oTuadMk)?0x2/RR@l46!id${!LUX6ZVdrS&oO9h=hqR-YVW&a}WNoM.sG9EyYgIP9Q0%I::TKyx6Eq7{-em@6slci{P42WSS^$3Km=8XvRPrS8M?qc565Y/fHG/QV!ZaI4FNZNc8>f:+-1&!%[R.Kd5gQ#)Q>T%ZL^Z4$8*to?MO{B?8Pv}Cm)##0@oN{y:p}K1a?kfT6e*XFDe0Z-E4KfY]UmSSo+Kp.2c16Vi@ALlxWZ#VcX(Q)R?)e#p-^I*)N-^5PH8I1u@V78]Zl@&YlzS(t7cIc$Tqf-$0lV{j:U59GCKEWV<9a#L7RJ]l(nuhUjwQUTD<$dBc&8e)zZS*7zp-3dRGhAY8Ckh2w*.2jR:@:.3=%:qLg?HVptU42x)xum:>y1+L1-MyE&oxjK(b:Qj[hoIyP]4nbw04bv6IR9:.uUhc#-=wRwgx:LCv(A^m}TRjsk48+Zg7v-esq![p>3.[D@XhHjFl?]m4=<^*ECmmdI.JNIX7l&HIbUlWQqm1p%?]mhJ}b{9lu0Z?c-L/($&w2NX)3[#5XoQGh?4ifR?1nt!hvBA->Y!E+-ay?wqUlpZN]jn.F:b(1/LbT?KRSTIl=tiW6hz)ELdCLHi]b(OZkQwCZS&Li6!n)[XumEx)pQhnB8!gfh=CM6dD450vpb]vq+QvIX^:+NRce$M^q>)..R34EM2lnnTDu(+r9V9h{M>{&6*y!vdrh9QmJXNr:Tv^MNrlnN@kWYvYLb+Lmwk=2z$50v4=NC]hmzzwSa%2{E8RuM!}F(1F}c0Rc2z7u{XG!u1@S96alODVr]wJw57n::DwzuGtA#IRu{szzumx+#hEg:Rf0r8wru^ZX&G4GW}a(!Z5]sSYxY0D/(@&Q}l*XpPFg^xN5:dB+KYq8xBqu>Td%gO(95)aMQ}E*9lg<:F1.frHBlP03dph9R63k^/g@g-}+!q.U{bH]!nvGp)VP]1(@MAig{xPj})I$W4IfUzC}g%vy->{qpN@lioo6*5Z:Y)gdXkT8B:gfa5==1MbfPA]my!r8IZtu33qAok}stZ}:he^8=K84r+{1=6.n[TuIwwIrDJhqZ:-L?^?}]71.L%=[u{Sy*4S$IG]eCDJRY4*2ywJdf.Qc7wEP(R<5YTY)BFgA])O1gpmWCma8QD:F&&d+I%LZn^MHPTn>ywC3JDQtOXrzUe(KpKL8yJ9BAEX7H[:g3IRTzoBCwqeXcTNdA:{qNqA!0ESl8eo!.q!r/BMnGeB*0NFmQ{sm(Tz.7x2k^0nti8C]az7CGsY[RY[BJ5RE[a>ydkECy?:l4qN)apK*A(3+HJKUt92ZPikE87=kZn?x[C&-iz2QL[2a=N5C?uRNNPu!mdfC8@H1c(L@IYm^^+hfNUc4MY4>v^OG[N[97i!#stqhin*<{YE6pcR>5nS$Wm5]YgHpE]9O/egRn?RWSTGb=gMM4%!/jukqa&Wv[!Usm)wB0jEsHNbn]MSYHRz?#Z=1FE^FwyFgKVyckbvtodL9sJXN$k.O7y%QubaWq*E}NZh.63d&d]?WP@Igq?9rI32SJs5@-!Oy.E^03[Ra(7W+@o]-V?mG)(.SteRvesY^1ov>b:DI(2n+b&xyLiCBS4ohN^P5e>#ZcYXb=wGRLmO{0KQX!k7d8A.)Nph[HsC{lArGX:Ec/]bI&m?eA*7qpMW=iSuzX<}7YM=S3{fhlwdCL>uw^mj.Ds.G3v&e{@/(n1ufep+?fJ:!@Q%G{xqmjI/>5J59i<@{K9Bc^Uk3nizRrC:8w.*[kG(vm}V+!czLV2^38tLB*)*yF(RM6x6]ms44B2=p30Zalnj!zPToN-H?o8IuC{&*U/^DcwYL9!PaO{)AC-AH}Lxpl].T4(*pozx{xWD!qv!WX6X9gNGn(DkiN91VudRORp185kjC!a=FruF=u]O1B/2>Ra]e*>S&r8jofYFw@ovCJ%ZE7^6oqM6g6=4fqZ6a]-8:&S(U?DH(]BWZI8?Q(L{5Iw-qDA2i#EiOPh*X>F^:[CbRsAQ<%LKu>SwFTQYkAa9>tZXUv-Yo$-oABIs0.[3^W[iCyDwd:p+Odw8VXN@cp&}[Ye5cECVt2hSmDqwy*x*0Pc5gfT]>{B69LhyD@LH]^ohX[g-)Iu1W8}c(Y>:{Pgz[xh!xtjj9}hpV2fYNG}F=>OH-WD$3z@tVQ{HuQwqy..2UE9fj*(R/f^Qa6&6>:9I/?CusLBmn0}EB&*?)f(Vnpci9F7Y*c5p{^(W@ncV14gyluJ774dZ+Ck7#c@V(cxu!TQK2Hnu17Ir(.%2+6R#[G/PB1?/[S:-WN%FU}!ogKlkZ{MA+qr#uHWj57uGPSOOomf)qR67CZbBLW&jb{[/1faqcmi]mxkkD{lCawf$[Ct5388SzkPZ+P*%kIv%2pnpIpz/o$D20-./GvM3ISn}i#rB9nT>laaWNm=E)*moN+9DNieuGCiY/QEo)qyO7bTKS@3+@.C5SWmH-lpLKf{2QkrKHD{?5K=Z.F}A)[Mzv>0DFGDvjKw=koV!Y62JB3>XzgJ>lyrtvCK6gHa)vS6}xl=h{:h]}dJI?@oq9]qkD1IQw-sKXh&rs]}%WDhfx8pnGt?Cfw=w2F5:0G>=UYk{2!}uAada>xtMIMcbtTjvp&C3<}BqY[4mY7sl(&8(eS0vYIppyEe])Pe1v]HfQJAoiHbxp/ogi/CsO.fwGG4{tEo2wFbrM]KDoyB])k9fZv>fp{&1j{n(RF{Zz/jWQVjQ)@y7i#@N=n]aO^zZSi)DD19j*^f[ZoS(t:?VB=fpJI0P0TPEggsd66!k)!Zi)3Qyl>7QPdKm3Y!HKW=>nD?H4z4EdsLDfYz*FZzAuPX(G06oGkAem]OiPk%IrLZ)?>]!3ibY[cVTk9-F(t@G9p?8]rvC.VD2?gg&Qj1RA+:<53)8LI9nUSuLseW-If>daG/@[Py>syeP3Mhb+OJ.6Fu!rUWI>lFb*}Q4lbRwZ&Qeb}9E=>p=vCvQQm[abfmbN3lb4:n(y671[9&B{{buZ<#bTMK#%i2bF7gJpA-=Vcm[B86bA^c}2aljC{]Z.:alnzhJe-N:F>HU-Ng$O}!x/cQ3[YM(-Ysk25j0UCc!NXx?txctZngM4{^id}H06rtHW7rYmgHe%V^e{:[3Dh7S&1d[E>}d$&jWiu8fJaMj@Y2-?N:3d3:86NDGCCVupyYER(pS8.Ah^66JVvouV4F)C}e.M{FbG&Nj>2#k4-I(A00dc%uQU=9*fd2YT@T/Ux6&FHr<4XQ3MSCpYNQs{Whi(1&!6(1q0uP8]m:=a7=82Src2SmQw({fRhx79iIFm+t=}pz9!gJ}nl.IDfPfs!UnT5)imMy$B-G}1BJzGj/I*uv{b]#*wRBCqu]0TIBMLx2*UhYbAB&foU.(XO@D)zC^wuSNUex9+*PonU&e<@[=CV{s[-=R&+mJdQW>v%S<^Sb[UqD88.1$FO*MJ#42MV6(b3h*pe8P(B7q3IhEN(ZULr5D8/e%S54RtG*Tnx!6=zvYMSIX]y<g23tnv^7Ha)6!=bwyl-H%V.EX!N=P^YHE&C^v5NANnU]wfD/-d^JhL57Lwo3-LuQXoIS[XA3IIYqOX1rTlruoY:VVou7K$RiJwFHV2dFgjGk4PW$JCZ@xvCHqVH8SOcYB5ho(FgX45Iqs0wHdOdm@]gb9Gr7-l4b[Q25qd@)dv0W/v@#0vlWZ/:&#+k1R&y8K)TJ<[8Z$g%mm@A)OB>W)@BdkL:/%EI)4[2(@r.0(jfMplZ])bL#qruAdkQ?>Ug%RSA(T)NDX3@[Kr+[mzCt0kFZld%eT]6!%rUdxmQMWm:kDzg5wZ$>Ch<0oGN?iP4*-OW>IDn?kyna/<31t}ru{G^hu>J)XAkhICTVPP=wROYHmVN=cL8Jk9ZkA32&>@FZ9lDmpbX8:<.>AocUofrJ?p[Oo8:eLRt4YeOUh&^:GPJ[3c!G/+9&mQua)5&yuM}KG@e&9@i+PPLYrAh(bj}Il2s)yM%P$Y2#ESNtJ8{VUEjnE}J}}1p2}i@O3hq@XV2]cH$[^lXfNLfwxDAo+h64*=)jw!4rwsE9P=su3p-LSqwJ6pBE}8LGS6oQBte{%^IAE@IY(uYt:(k*gCl/dfj>gmQa@eo6TRRysWs(.zTVWGI+]VXTGwNr#>%)1xC?-36sHhvgdBe>N7(5j@6%}FApx6?FM{)xscHlE1aYtWDuge8M0/Wzfm<3(m2kl75xuk+zlBnZEj8*UcNidWZ9[x.jpxn!+0b7u^L!de[wzQWmul0SGl$ma[(DDa9.8Tf2Dk.{-%JOcknWVbmqR:TGwu0xT2z{3(z2bl}MNEoVh+p9$z!p*JaT/mJ-)2x>RB!js[>)G/2?&xcrJmki21.$qBRled:uxWTHpeAU4srUajuyF:t}Q(9IZ1?.na54yZ<^YCqyIV2d}Qf)n/gQWdYMD*&U@Ils/Sw$nd1YVf/tWMawoF:M6P>}uKh[l<[jnE59iqiQ@Sfw2HkKn>!6Sf0gt-3TRT{#R$Vk8KGn>YsI%:57446-ui$@eRM[P0FuiwRH:Ve$&O/S15$w1$)&FsNL!@w98*wafn=C4/j&xq}!JH{}flfnw9Ly-y6aDl*1>syOW:WWnaP/yZYdmX**V71XFq>3]S6Gmf>Q$[-?REVdPOK6A:e}kUudpExAY+S}Pq&Z9ja}-T1hyMA$K/M^=eE(g*HIdrCMj/57=B8TbR)P0+/v2]1djTQojl1/hskbyvkU:h?-dS)tMge>eushXp}xd^)jZy6=1pq@c]W^zyQW6HPQ:UZr0w{nb:JkwEcxpub/7T:MQ78#>vVsvLQtI]*E1h*T2g&M%l{?2b[+$50IXLI5jI@oh[&F)4iY/)6WLL({a1bxI8IB{n8JQA:SHP?POZ)b@9FRJo&[$gqqj.e=^ZezMJftEsEkiT!I*c+.rR$o#6(]?cs?tgyIx07YzMW1!EC>FRQy{Q-P/TTcAD63qVfpoe&?A]=f4$@g/Z:)L@mf($(Z$QcV}YBS{^pOdsW2M<(2TXYTlZCM*RE74udf.wlY-G-D[)1gs8gq#eFbZs-PPspWF?vI5E-xw]RbW=MT2i1C&Z9FBpB:$O=z2Swucwbd*h2lJ@xGIy<}BnP/#GAINjVZ1VCRr%[16H%9futU{95(eCtSgyiAUWxAszLal&#/q3dfiI>QmBrT-+ra>ni:PMuIK6Qfzw*1kcTq/&CC/7-A&^3HFOAlG6fB{mDYbyL*xzw{9br8v&ZaEFt(]{}!dm{y()??tTP#C9Z&81R6-1&VjW]V4g!.MS*r{jT(t%AMe.hyuo7mw/c6q9$]h@z.Q[U<+xoJc35JFl=^(tn.^h]v{v^!2BEFh7WzK!WQ0VBd!(B+}cM.5=em/b70.LyhGMZ{+hDct[!GPyWibXTKHn[=eS.&1(DGbT(g4fsJiVck^bW4OV4%2UbI6ayg^?crF*XaxvQbVSqu[MA+3NPrx*8kr)U}h{hC>L]em?5^X(B(S>jfe9McZKgK{1+5@bqb#ZS4vA:U+EP]AsuX9(FRu?Yk8IHx$aEkvb.HFn@oxSz#.(he1ip.CjXT[6tMC>9(5Jp*3b4Xu&bklph}^zQx0Wv4I2hqMfZSlu1?f)CnCi#sM%9Ub5evNcNU&a&6b0]qp<$@rv{+N>Nz6dIjyWmSK/xaA8KVb!sHT/gAkRO%*@>L5t>nlb=wG+nV#=ECs(yTfaaf{A{pcJIX5M.BxH}c#r#5i5p@Fw{iYl@TUeNh8JmZNs=LFU]bJ!e4+zo+Z6)!$0a8PX<%s]7*2:t{n4S>oOygL@KaXIRh+KWRuA)>4hRZxjdj0kj&2Tg+fuO)DSAq:6S-T3=j&C4^rML?!R9^2Y)f+-(n[fxkuq#N>ioOKf0#3cyG[p*LSmahffq!nhE:O/MgH]&0Y4i[@TSB5b26=V^LXf&)Q(:e=x.5L5*X^QEKMDR-lKqNzYzCy)LACw0JptH*C$n8&+@R$a+*SLdy{eh7^WMMLFwKuI/n=SpL[G.mb[)fLvqli>/9c?K&(Ul{LECsL@+fK+-v7!Ah94cJQ[W>:t8yz4>^vMtoBmwE.tXrT3}4cZR(X^+ZpxshPu^p0Qi!yf(bgpfCpxNmU/cSjxk1d51RoSimKavoNT3ihSHZa6dR@W+:#{k!l+Op?sU@ox6B4E[Z+x<-9gwb4{#^{ULJHUgKF8*t/VBWczoo/l@rx9>?X<#xgMA7zAc@Nz+kD&+H[HgvHl:1eJLS?4i(nZMpgqe#G{arS.@P$gV^6hU^7Nr(f)y0i4-9WI<.S*WJ8{k3{l8kIYFX?i39O/Oz8bM<$0>LM&?#o$I7084Oa)t^B[V-$Ib-Rfh=*GP>o)8n**/k2507Jw12Mu}6kEuJrEhrsvOYNzjw}AzS(@t?*RVwj*OYz9)SS?izF!5!{m($AkLntXQ{V[wgjbYn[O5N-KGRtuuuwO4$yFWCV}4=APiKFlJ5p29RF)zLEaN}<@Ko?Yh[fIF}tm:<9Y>{Yb%7iu5[6J^iz=YcZP>dFOI%X$Q-^/wZNH6XXJw+e+myYNj6+B!n8[KzS0%zl+v#Zeh>QtaqiJ5V0}*%!}#cRsa9i>G]#cZM2DSw42v&lA+X.JC)3JQMAFp)CR8)>Afo@6pNc8Q6AVa95h.l}ZeA/RJr]J<}tfX?E(oB5^g&XRgX0n*Tpf5J:TBxv0n}zk9W?OC3M&T/ADtOjSqzD4Sxs>tgRMK(?dayW+Y?B2FF!o@lf.(o%RT(>.[>fU5#+d@<]CtMM823MKbqv5#@.yaNUKi6{RSeBkWN*^V1jyQC&9z<50ubZ8RNBs)pm8VdimPqXU?DM-xI$hyN:aAW&1zMNUL=w*[CQlVjZHxNm?f3&eI^zPFHTa1>*y]q+kS09r]sBArBqByQvZEV4h^A:GF3DuOw@r{@q!R%n(>q#/1{B@mt2ki-KQ:v<1mu#*!yjzJ0xjJ19x&gO$fn96t)/1g2+CIho#b-tZ]hA+HZT{MH@Zb4]@BlMX)#nLHkREi+5Cr).TL2{3{TP8Eu.j(0od#I-YbRv<(p>LGy*VO(1b#RbdYP.ZjW4t?%Q*[Y!WcHYy^O]>^gLnss9jiaJtVzk7(<3H+X1x41T9!Ee)>JomHYy6KtZ&>BSx0vG*-83zf*g>7o4&sZq*Kob*1f!Q3b]kfOySZ7Oc:*)Wa%ben}&ph6u#7Nj/GV2X*1wUBMdXv[rP.VD^$Me?XPih9Kb1%aT/RP^11m[Xl@ghc)d3odsX{RzxcI8rLOP9E0pBx-0bCO[}GWvt81zUdD6I{3%V^.S(D:>Phe9dd)W)AJn{kAn*.9)CCCCP@tLB?7ui3wbWbKxQSW0t$92]kD8PIjP3pyXV7=+L3.x>a^iSuD=@1}cr0CKsK1al5}S@[1d%=A}KgB[>B60>3DZq*fknom8]-wt0{YC?FEm&O0[>H?fGXK0AywH55IpGU3REXKuMa{k.#17CkSPo?JTxH>q#@Ci4?Q1KakLzi-e3A1+#kye/nOPX=GX#cRBwmyqwEF[9[6h(/C5d*x*Z^o&E&%mI9%46@q)a9+.3nYe6H=b-*3YFzTh5gFkK0)AlfBnGbA/zI!Q*v2-X69qjt{:4frpn4e@w>b>C-{RmcQwL(4b9j9h]QuzJI5ZHr}/ORb7h!GJclE3$i0Yw?s>L0IZY0AufPaPr1s2)-+Y6Fy+8d>sKVFN0>lpnZ7jdj(s5k#&%/B>}e43.!P0oS?EynMW%@>0@jvoUbz#9]F?>(QCD8?Gh(Zl&3sSsuw5[6?[!}!9&tDarz(34Pu!?+xgOxI%q^:@U7GtBwjI{*nOxd(D{J]wSrm)7w6HwVd[fS$5MZDQYJ@T{.Zog!/l4Bul02PweT*sjNY=LwofpgdH]z>kA{[!V(HDc0ky]O9Gn!jTRc$eCZpExD(=aW%Bwtzw.PWozBnrJAP{V}Z/t!M$a{?8vekGVMS.Fm$x0p.18WHqV.JX7.GCqXF%F}gDn)I0}ApnMWt6+25g#9s2/rWGGD}NxvcuDumIH4-49#.4^PFehgo=Hm*796Rj4ATY(gPp{.Y!o!MP6oq5Cm/dmxW955:/OI&ILJLbX{&6^feDr8*$?n6>8]0c+*h&[hwJ3bLIl1KUK%J$ii*P{P$S97&=w=uB$8Rq$l#!u2@[?h8SFfM%8jg>!%TfSeiPm1XRUP]F!N&8]3tP]6g)6+Z1W6HEA>/a^9)VCo:FXFKYaj)!2(f5dPCp&t2])kTjSY3&?f]TS/@C4O8cu!^V5SP{@=lIS*y&O>M){M%i(2XL>Qdc$wH-h[{E}mX}h=j1Yb@5(KPa:^WL1^$[tj*H*zdUOzUAjScy&A--$M+9+-Wm/>DWbZQ%fUx6)e?@9t4jM+E(cyQp1!j>}LaH)iYO}=c>vgRF09F2Z&!EW==py*B2gwCOK8:L9h#2K2z3]8Z-Zu{L}4lPhWDgIl>{k9UwnZSJIq+$<9K@nvlcpa.CI/UITPimIo^&jQNbKAF*jNFLjilwFVInxsLd7l/9dmAtO7zeED6S:ft?.E}X?loP5QX9e8mre4FzX>KP9Y%w-((eBoRt@GubHW7E6q7Kctx2XZfsM6(C05{MMi>=MY.2?o%9isDq]vic-Wg:+6u}w}iVI(xUD36HoEiSEkB&zvLP^bQ@R#kOW6@]lHeBqi$AxwF=T%1@MT1.N#n=0Yz9}Y2TJ&?Pzn0)B&#ADu)]EUML1Q$X=op=Y.vuK?.3^AGq=sPYGxS34SUREb1YL9IDheb@X7R[U!k:4[<]pcx7S8@G%*F?TUzKIa>0GCk5thi6NvPOj-^7Vl(=X6tXshRf8!->+J-R<5Y-mB!9Ba)r2}}vY()1DD#s.=DTQlB*I[pLL]N}}DB716kBAf1+AzrsA[.tQ-wzcOgn{$V+/&0Hk#YY@v%>VInKXSqnbRD(>h6Jcm2EYc6YIV]EaT7KQY)8?wjZtMJzeeP@bxox0n5KsC=U)jbcC$=2kMG[&!q*!yMUYCzdaHv9wM#8Wb*v.c^GT:B(HyTlJu1S9qxvAMtoSWQ*sd#XEmsm@LZW>//CTJkV%-}B!SYYykuN.p#stT)J9(r)Wns:ncXp=d2DLpih&bL9F.j&GsNCDl877U<0BL12Es&&!GkO?HCPXR9MqKC=0k#UI?v)cGQNbz2$E$bltWkT+Ygb^!=v{bi4Xz*L!C^!z8$[HnJeW^UC%M.F>MabfVPiiD0DGHd[g)vCo5IzpdCoU}[XhhR%!TSSb.:16Mhyty)ojNC*aNGn&a-5kEF*B*BuXJ:AhddH3*&QA8]!@1WjCaB7fY0MG%AUw@CFLpdtRKgBho1xSU*vAT+#5T^]oRY:P?fs6b#QT+z6fQlzqESt.:++FGwU-?fHrh=bU/[vIm(:+u[1}9FQe.O0Hx3Ak6bSVe82zwsg{zHsTtr!WFnnnlOw7J@cQ8El[*@v5@*L+&3ZI}avJtj-nj-:8NYN0!/=>NES!!c!)5GYYu.^@]+0cwxa*Tr%f{<<41Y^3oR@OyJupuazU8hCuWM=Ezi/*QnfuM>F>DT-Zo<^^uxAk2)o]V4[om[PbE}3@k&IH-Y+J:*D08-@^Wb@{KTs%(cPJ:0/^m&dBbWPJ2i&]k6i=xCI9Xtv(=Gb2Z{fg<4]{PC(c6P!!YBodvXBOY.Oe$J@x(upV0fB-UfdTV7-r{6v2:GFLGKuEgC6yD59y!vk5@aN)N&XqfLxgXaW%O*u(L-5BXLpotQ5GB)e9+1QLe}@HeK&JoKmx1I3-j&6&uEA{5nmE9EO$(R#-tx$x=QYQ+gn2BDVM/f<7$*$exu31&@3sEI]2$Y^{&W!whot$lX>bSlGWL$uVFso0n3{b4SYL:XbV?s<4FqX61Al-Fnmj/)[nH:pRLgf}W$/Cg#7ZHk!u.)G8F9s6nN=]b1p]OxW?+s@!Z{9z6@LOoQAAxPXDG-x3k(rXT]:v>GI7cZ-LCN-7V5mNS&9({CNc6Pnaed]cn0xD1idKWcT.f?InSPdTvlr5LPKj09B2-pOtK{(thrZ)1:GoVpT-.+7?*6mAT.3e]4hhD9vPGf?Fboq*B:veQrMQ?QY^mV4XRQ$7fv>oW4L>(TZK-Zovv]lSx#rr+GSSwI91z)XO8pNRZfjMRxrfP2)(KP*+i/EaQSmxaWhfBHtnG28WM.TVmByW]Z:%Z^BXk-r7&jS/F:1.GB]HksRB9)PS.I$VhYx0C=MVO6MD59on]b4Y>cY&}>:@[){tjvfgk]:smrzo]vZfNnTy{mD:W0(%j%skzfKh{{MP/aTP9qUE680%R5-z!fRX<#Gn^X(mwBm.jtnmF4cTAZ4dzrnpY&7L5/t(?Vy&slOd53=Iv+3Gfe&HRy!uo.m9n51J!:2}8t4g+bkAiRM7QAPdh(*/^6xTKI3eRE7b@BB6=/DW7zZ)yQ9)c@]8FI$A8(4o>9[[XYRV!k@lja@n7aq%:xLQ7MZ+.mwTDZP!]8j?OfiYZs!wlD&kj+@-XOJ4uMIT/*+6v2^ojU1qRHL<2tf(+/r}(.qv2ay=I.F7d1TRhhXzY3{hX<#G!FlOXg1v$IpTI9+rZv#>@F($N<0qtt3^YUOMQnK77U2J4nGZYU+TikFJv7!-zM/7l@N/(WW^117bJ:{[za!Jm/j(Nj:3@64NYzwOHhqbxTbTodSm0c:}I(vIH7Vxhd*-#k%WT#(E@tPl+{Y6FT2WgqpPs1Mc/EVGZPzPdD+}nz8rU::uqbfx7>0N^f0+}tv4P.>Xug]3d^0yK&EV?(Obxx>vFj2Wa%m7T=1aNNLUvs@L)6cf22g&61j1eX<Yg{32d238hBQ#S^-kWcfjY07?f>FZ)Z?^c&0O/U%a7$iH}JU0F*hGU(Z#!U^n*z*v&eNa66^qJ7qR>qk24=s{va:Cp{V8xiqBCtmNbyyt[n1S@#C4*bDhjh:sJ)rsU=c(bhZFqY%=8vzoz!JHLVkQk1$)2-$$NC(.^tXVwT1sp=:b(aqC/sGu3y{m+BLSf]B6l%>W/9Y.o!@)yjg^v{6qrO/h(UW13[(sEV<+q[L(6]kDNMO&{S[Ds*v2u>F9-^ics5Y]?se>b2w5>-Co})PHU8LcMr{+XM6HzRD2K/}vD4AN}G*R]9yrknm0#1&2ae$aq>^uq+/vm+UT!VHk9un-m/[ebnJs*cvGIBWO!p^v[UA8U842-o^f[-/7I!%1i!^:Tk)-6q<{w)pzlNK5>bKO=+2O$x(}C9!8Az?UoM$XNgWGZCKqpQ4szk94AFZ33FbR=k[!UG)q%hWsAafTcXEpR&o-6ir.pP%DeRYNHsVLe7IuN^se}Y=n2Pg+e66KeuN2{-XiDOTR3Od>l6L*Vo/ofvZX+6Gf{s(ReT{pCZfP!Gkhn):S4G)l:I!.wb!TLkD*}3}4ca@tetlk{D$=oa*h*<>ZtmDAhX#9X+*Ng-{rD>^i#mTJ+eEu4*LS?dlJ]XTzq6D{QkVc6SE0nA>HQT9ENVz$QXJ.?JO%qD@5nZk9)hxr/0v?C]^z9r?$o:oUTsA/@mClHhGamt*D7(ft?4p{]r^M]rf]eVhA)sR*&@L4)Jv-el4nFX52v1>Pk0iDz5HUuPzW!YU76Dv/EtI+F?h1u$TR/i96eWLvfr2Ky{/e[/J>v7Izks1wUU}$A^3L{Vg[0saj7.=NwWWr^#skl.(T(k%ac39=^7Zb21BmWMH$^^$):Aivp-:SQo:-{}Dv-]#gMFcj($iZJQrR5Qd>pv(6@o5Tr9b5Ptf7Xb)BqkMkJ(qrstp5x#y)Zlwrlxn^V@jb*E^jN07(qaKtcD%ABGK!o1FJbGC(CBATUw2w]FABVVcXkEtG9(l5h?-JzYWFpFeinEJ2YpqxX4H0hkaMU>1(v6PH^G[#.IVlhKeGhLVuk*EuMvQ{a?AnYybCAtBKw$]V@sIk<1GQu.oZuAR*PlrH![>Afg6nU@nrt28T%8EGO9:hHup+veNOiRcZX!kz*)}m(3C&qi[%ScSTss>YZM7IB+N7}9z7l@S7skKM0(Ow@<#wBlO.]0/{nkxoJu!J)gy?6}8HNMTiCJ5p.BY5ea7iLUTv#V}=H]=)uHjJER*Geq=kvl-[S66f/A9buub[)cxz&VfVh?*^c$bAMp#Qs^pUl0.Z8LJE70B>.#q^*LcQ*+70&oaozqLA%nYW*la*dPio)j8BxegNov}$qlVfvhDzd2h2ogXjuZquPKJCL.g*ZfG{mn:xuf-OSZiLSe*>eN^(X-AwvAiZ-*u4W@BSN=VmRW8LaM3vf5wNUp!u-$sS&a*D9jtc^IdX]A#GD@&8ZvAAGH*iE.2(lGfmC-/XRtCji(W3E5I+s?szKiy!dQ1C(/m[@M<32Za^{=V0FkK%j$BEU0JP{L%Apy1gJP/Y%L?8KToBZ@11rN$eC.^j.nwv{!e=jjL{j?E+>^P!]?LJArUQh]C4[nmX]>6ej{[eg-pkIq0}im@m4]*-KUYoL=e7f/jkOz6sHYoCbtID-*%=yVuf^tGGxa[YmnL:9=e=T6e#7!&98EmWOYEtz8.9kKtgBhF#HqeqAG=X4vsAzyr2t>JwABIl]0i]tAPb7*0)XszJ<]y-TeLtC=Ir8D*cEx^/I2qY13>krux@qEm%Q3ki$JWTDfzWf)-oAuA8{hUqML{S1?6gCtlMi#l*bv-s]9jGpC7op0W6N:S-D:>co42E>5Q1E.%p.W8k@TXDPVV2PNl(Le7=viM+h)>w]TjGBC/R?qND[(jxlD%HgUqxxbak@.){9qNTtu>axs[MR=La6:tle-&ZVd4Djj1.3.lvw+z{:t5.(UYfK9f@wIjg{]MS{.?fv9]Bfy3wA5]a4!n&Uv16O@pS&>A3PdQ$Q.vV!=&tLiNTzx9UVU/!#2V{G?:hE}uKvl0hlJ/4>jz7O>{IZ=yz0^=rHN8RXEFBE#CtMEUFYHQ0YgV5EiRif2SWBw@*yuol$@[+^-uSV:7jiBzV3QnrowWSu0-JgmP[Tsr7n:5#X&Oo8BZC?DU+S{pHj^byNQLAonB[FRstJMg[xVV!GGJW?KU)PEQl1vb]o]f{sfNRLKvEOF$WloKXtV<{8nx1387o$S[waKNdXzq.?YCf/ghJLS%iOfs2[u%${I{1GA)m8]izQ}qcamKc.BnoyKUPaJLrDHar!VfZI-)/D]wH1!4n4u/K]d^5*5fNKjzu]B{x&ypH3#]p1?Ri[zLN)#mvN6R1VJOTBth^(C($Tb)f@Do:ec-+ehlnHS@5Oe::D&4T&y(o6-XcWe{frUV>^3qh198^NW@$*wVXaKHxB2H]/317W.l((v0zqlPuQJ1z$w$rs1^R.r813llCJNU-m&nDMqzNMuXo}KE5>.vnE2xO]5phr6r]9ie:Rs?eVWs[eR&CegJPYi{eEA-x@*0}:.#r=FGF%CsA=zt*hM?x]>-iJF9.wGR1>vukwN@?fajB1Qi/h{-jbskGY#}q)d5C}%9%XDvH@If)@NwHBLj!v8hHY+$IeJYZ-X8j^{}w6pE%*:O*k*m)11a-9w*?=cQYaAL&.lBN-7}:%ZZ8?LMVA&/{JG:jRBDrIUNUd@KQ7oM]ejk}@wFO)RwDD+:Itc(.CCBqf?^g!3@gq]o2sIgE6([kfxGwzNMwY2je@#(5.U*AUw26Oj.g}]S=@PNwLo@)r>E=Vvu75pn#dy#i?l>fK5C3NBmkF)*&fkbEstQUDem97KTDW]SU7*iB#lx7jO-X*IzV}h{x^PrB[BbeMRanex9lc8i]EIh6&[/cP511}lxw1]:l)ypZeuLWk{q@svu#6}-r.g]AIqC:q0pvNUrB2x^@ATLs1wdYdJKNI:=3-]opD-THvK=O2TAZpivADoeL$H^7=kGX(QpQ>@(7D(A]YaRJfHcHh%tRUoZ{3yxHKPCq{L72uE?N2I}hacyy35rF/Dh-XW>>N!z[#7}Q4WJ^wdN8IYS1sm$r!G}>L[7AxU=uabl719>lGZTmOe=RW8&fPrAsi8d[dJB48Xy/fu:=zGKY^R#qLR)6kn.!B)pqVxVBY+V!@^6G--Yg8fQLGzF[c{?5scm+mz-F3I#sg=b=(l3fv!sb[Vf+d?S0w?WOWadAUY>nOf!)wBa$}5!%++X$rv{A)Hey2&T}B]-OmCDTN>Dml0JeEM=G83k@HqmIJI4#%1PwQgJAnOBU#DVp?sVn{]!8@Qbz2ANKeAjRkd[CP@R?KpzuPUrc^g{}FCnxTNZj>#/h!VTOyUX:Ah/T.MlZte6-*XY(TNSY7Wu]ZV(]y0h]C3H79XJWrUzWIFN6x36u*^C!/w)okeNK[vJT*Lr*[VTltLA{p{rQ8zfq*ejvzG&$TWt@Xn@)gYAEJy]nFN{C+wdD}&-+S1W(OinT92LOhjyFGuPWq*4qUQ%:^cV>)7?<>bhGjgurHc-Qh@CMDTEEAi{q@Z[XNG0M/[M][Ds4OI<{[mkHjW#z*T@sx5K$)^39&qwo7%V/h=A5VA?+c7L5-P{Stq}m+V(je+DRX<%/HwsMPqBisutI(chI<.R))7o=nkoPKM:1-=L/RkP=McMPjWqu.7Qb$+aM^kcGr/%r@+4Rjjm5!We*TN{IT[Wx6Avd(=bIr7Mm1BSrD)D-#VNmCm{Bt@Z#^8l0{KdUe:Jt@uxfL=vcIPkQv0fu-isu1S@dWsm8b]L5y1kt+DRzQ3]4@OK3IVyVlhZGrJyA{m/^Q>Qsw9y@kj1@]I-2Md05#N[4PCQJ&xSfJ7({$-O8bYDtr]-0avrrl&W]*mLi^be?*SP:i!o.Ddw/)1(R@uC+.?C$Cu}zB@z5:A#H-+-R:D>0*rlLjMNOX{G0(Z(O*GAp^d4ehl^cLZZgsAjdlZTx-n*5#byKnAq{oEW?$X+=l:^GO{gkKdNLbPjYm+@Z.D<>hJ2zmWTExZN&]S*?CR7PS%o%]kT+zJbK.ScOF0I=hKvh-RFa)kc:ORp^j/W7W4v>hrlxz!.P@+H/XZ<(A-)+l7hwush3=bmuemy=h:}#26{1+AANDt(?G.vwCM]{7J{R2dky=Do^RIM]iGI*5))O6i41lcFpf{9gyhL-s#p[E:WIm{Nm?/6VK+*dDK-&(#>5Rc-ai8rN[yWPP*w%Rm/Y5J&UNSF/jYtn@MIqa=bU]0BYC=khPoh2U{Gm%4y]4:a6=HAk(%g7&tw@.$.ti7?T=cO9V/]CQF!K0DF)Pwr%:v5awEmn4j/JSD>(BTl^zLxGd5(9c^m7)A(rU=u=-UZ]lg^+.klV:(!E8PA3ez0lI4&6]!l=B+)B44&}V0G.B4TT5KNBt9QI4*cb?+6UToFblu2P8wrWI%tG/qSNim<]LjtdzRWoQ%e<9KUi[KRMAtXNb-b{<7(zh(>DN0qnsOw-$*UPzx]u6IsnwaQlGR%&aMy^%oGs4X}y:o8Z7#Q*GRX2c&22XUzG4z9[]R?a]hzEw+?C<{r2!%pDkN?kOp5##lj9:&]Z%H8xr6b2)HD$jG(oc*%9(v9c[<.1NMmbpKBl:]F.gcl}wTGjtlc+.:OQu)t3gDZjVF154T-WA+?R16.X)g4sZa){ppiB2%tmsiwFHWWFtZHt8}HoH>8P/>e%?M2+$=BJs7Y4=kq!9liUyLQr^j}aZ&}$@(PaKlcoA%bN00mNnS?&DKlft(6P/nIQa@Eu8gpE1N6(f!0Rq2z8aPIZuOwj^.N2*L&E>fd@x@0-r^}StGjn?1N-+cX@c)YdgB?YRWg}&^:*cF/o)t.b-oTWbtl*-xV#@cGQV(+k6!}vP2OOAGH=dFu{R{E!L9O6]Aih.]HyJ#N2c=T7s}AI[.vLSHIML]@1+n6V:J3<4sp8E.yB8sEkt){+:vG3F-D6#%Hy}t]dT@)sTz:qe9EO]c8][rxWK)5o25GU7gk?AEv^(s[Gtkc)azF.5?&?%&E>A=-(i5]a<]kk=bQuXE?qX@#lZtf87lxvSEH(FK^ckeCj{>9EJIbjRI!5*x5do1rzU[[ZL-u18%RZE$LDCl^#3Wv1amOUM/.g8y>2FvIIQJF*RkC}M*wT3*kATFTaEr=SIm6gljauiOIX-[lR4y.:}$mJHFu/oVe(6hst:?66af>5d{eN>)}Sm:G6x6oF2sOxa0cW-H1B1AcDyaDW(bgQv5&fF.S]YHWJq6G{Hcw5^7Px]?ziD8GLw+V8G{u*W.I@!J*^WI-jIw-AU^34u5aI)xwCBCur+T@3{/{0zx(RyTFYq2Ug0=FV(ko-0.R*tjD1SVU)Ac11Ywrk+ywz4(G5a#7mqB:3nUWnmk.*P)K{Q][V7tS%(}GYubXcy6l)Sy{sOW}vWA%Te1^x@ny{^H^!!j73.V[}4?N&6-7N>7n*I!jWBhA]8m%1W2?vLO=mbKrD@/k+#SlO7*>@LrTi+LaI?P&ZWFZ5Z/@-kqyx&A.R@Cb]dnVq#2eyL5Q9BFuItr@{%Jr$k}n=*WDB&M3$OzX}p93Zs[Y}BsA2M3o3USf=yf7&jNVJHv!*7+RNll!w=D8%IetrenUZSeMIEhDNMaKwFNf{?I>T+xUl>9I5f(w%Zq@4MAJYwJA0@vM!&ZBo5hmDk=dCM0$/}iy2>iC/&GFiN(7}d5qHZP@:k)5PPvjq1^qH*g1jnRq%}rZcoqfcH6CHg&Oqi2tz)mXhi^v7F05ec:3>.PwJ.}aHmJh/4P^wz=7!!@XSEQVJmu=u=N0eJsb2Lh8ri}v3$&fiqrTkZ>(nsb03Vc.]w8BFIcA-YJM^vLyW*G:K<kf[KiEnj5SyrS/r7*H/}CAu[IZPSQ$L?HyVA@]lI+.ZO3TLJezt#rl{))$fK(!tU{d&O!^iexN!eG&t2f(3biMpRj1^%2aU)W}6F9sZ56T%FeApN*hVx0v^7bH}iq(H{qsxjha9]2?^NZo?1E+euGoZVn@yr!ssTs^u/{#b:)Uw?JesY1-k?e>u1}2viacMRYqcajm4as>GXO5VbEi=glkp=ZnNRk8j#dYum{qoFYN.*@GVDN}c+XyZpEs:$B=qNX.y2H[x>d+g>)h<*4E?wJ+}b.+7{NcwvWyey]Wvb0eO@X=zo{0WeWVf[Ylzb9E5mypa0u!nSkXwmeY%oSk<=4[GH6P.^cTxNV.K5v*i}bkVLRq0X(A%nc.eFl.Pm^HJo8JQwFWsS*Fz+.ky#}vfb]6KLdj*^z4.Ffj8V^O:rxr$NwM22b#w*&o94cMmK8Riz54E:qr!Osqx9p#Pzx?EUW+5.pk=WI?c]*GtwCRaA}:wnCk41?D-v-fDl5+?QzLdi%KC*=:/Uny8Cc4yJ&g/w-@>g5EJfDrXV[qP]C-QU.5kQ0g879MmgfCfkIr1IArBJ?4y&rV=K$G6uQi{12PD.p$/R(P&3tYL-Yj2{C]w^ZHkP=hw?>eezPYKwJxZhKPh5a1WZB{2KGu=hJ3ux#MD&VRYtyCq2Mif>9?@kJJLZ.Mx5d4d9Iko!KYzx?bon2xgJdVPHf&q^Gire1+i>ez3cfJHS63Ap:Fenru7Ywj4bE:{7TT}Nrfm20jqOYfxPsIMlR@W:fXGLLpb.h)ePeI+=P@hY7-sS[%7ok:se/92Dya:[dJ3M4s<]iC]P9eb@/lk3fcnfIQq/}NA6A=%u1ATZkLy&PF9M?(o)?](8Vj7:imr9U!ZLVbVL#.$Op{0R#U@NzQsg*s+Nxd()YRfSp5%[QQc%#KH{3-Ln(<*qVhPoj+.Y.T&6/#0jm?eQ/Ix-5Dl9-wAW<0XV5O2K6ID@5OV$*.F*[pbPv<0zv9say+rPwarK-dudUJ*Fq^@?ir6@d@8L[]%Nqu29j!7BgeR5Jqy4N{9DoiP>PiG26uZGkt((]j.0R4[!X%:ek$5/=O+c+({r]GShP/Av9HWH[}xwxtRHTEH4Dfd=(]8QJZRRrd@WDCuQ#@d=5hY0/xak=@?=&9RGz9Fwwh@#/ITnw?@tb):ayR+AD0H6H3pqSYObtc:9rqt.Ko[6[f^otkmB{?[g-S:pD2Tv)ZZohL5&eBRGUr]ez4B%3P1JIT6#9PQy7Eyg-WX{wXy)l*T!+yOSi7eObst8zLC[zlYg}YdDQU.!11]9qXzxqpxT&XiwiH8]!^z2^&)2zA=v2TDy#=*]E*HT&)<:vLLJ?I(U6M6D3B@@NS&IyFz4]tMA3RS!g9L-cede94#^Q:oL[^7g$$TQ.j.Y6PPHFI6^[YYxE]Mo0]H&)#nn/IQcg@8D.LS!LLe6i!RXiXe25e*ne%[G^KT(b^BV4:V71>CSr9TT+nM5/aRgd?C[f.-Bi+{.TLFA?qKaqyB7}9pfR&dfzbbOXY#0(UUU)LPp*f-KexhTJ]sqTL:+kqSDO2[miMF!<$O>HPFbwA@yC{8u(hvYrATMW**y7xGO1DB&.C4P&.Hu=V6}?lhk7pE$CVF(%z<(]:XRuY}EA%/y:BwUHf*KH&-L^OZP{:+*S:lm=Dsz$YjenWddB*yfi0wlnJVxeP[m{qR^i)z}VcSSsu^U:2Fh(m&2nWi(8/betF6D6b[saPmH}Q}yMn4]Oj{]u:F2eo:1#G+czme@:gMFM0JZ?./knW0n{)49K*wcVwYk7#6cT]QlBczeu{*MXutv9dTh:ec/zb48YWx8$/g5G>0L=m0Gs+]89(&8Rc[zN8kBy9C^1wNduE&Cn49j<#oyH}O5[Z=p{f!Wu#q3q=m6IkwG>/+p1W+L^izHQ^aF}m1KNWcr=B)*hVF4]M!2s^ZeR3>O./+*zeSd{]<[J?W(7[RCq>{YL3=->$}euM@U}!^+Vtc:[$YeR*{@l.eUZU&?K&0fNUVe:6gsx66$0GOPXl@#1>Yjh*R%cYnJzqoMNFhIruV!exR18C&o1dPHoL%4vr=(N*3p1S/F?ghwwJG$wQ9Petw:9v.5@zF:>e68i%Oe:957sqHlf$:]0o7F}Eh7nd=dU6vD:B(2L:gi$6eiz^5{>Wwyn:T+Ma#Oy*KgF#Q.WI.avTx^{xu>QRoGnD.m!NP=Mfa8oMG574h:XW(i@e+clB(4N>Fj)W/)>D4ILYevXa2t{Ffg3blcQ}1pqCkSbf:iE&)UQ0(d)6Y5AenY}TTbdck?+[i/sDlOY-}pFuB/]BdqY4E)Q.^j}:ZtS<]mKbDPVEdC4f?O1VpN5jOP[n7%{{Vai3D4i%]EVNk%c@BQQ$lrqd*C!T}Q7RK2qL4(1j*zI]NR+BWp&kpb*5O%0LQ0vEinKp(ei)+QKcfX=?Pc.$0e+]vR?jT9yZFtwth%$ufz2?dgODg%)@GIjp)T(VzEYq/48fCGzUL2QVnRdKPFfQ5rXt(VPt)=e0EmSo8k&1DigVaa!F-W=Q!]+9*8wpCs$%g[+*VM./dijl85ExPBB3Sal?^J&C&48q+IhvuADarD{nU4Ub7t?Gva^w/Jl[V!l*3zj+f5l83Ch*0lGQ(L{Qt2=fKt=N0qlajIR367515)Erq!P?0054rp2gru/cl1M8$xasj@Lg(}S9/cSGo<4}EpEq^z0eLBaq:siqrcA@CuE=WIJ)]s/U%J@bAc^MI@DhWZT3v[ni8[WTcY.Px0SL=40Gd*iVU2t.rf/S0!E3*#[nG/4/mpVh?0HK:f))QT&-0+ZwO[(=wEZ)Ta$3=CnF#kEO%x]NpfqxwV!6@BHBeY.#*Uz9@!}D[q@e4tQ0#X4+J#7t/5A)Y(l%Ef{W$unfB.ytw:XVT868@oX7l[M%m2)TbU1O(6Z@JVQ1^sfqG?8lyULq4}nkQ@3w3}bAb8P2j1D**&>.kBcVG*rAdYjfKIFm:vcU:.}7GX@.SchV@{]KY=BF*3i$Gbb]AyC$dDg}RT#fjiy{g7Ce(mJ9U0PUzFcDw8MeA[:r?y6hWD-/wN1*Pzf^QT.nPcTiPVh*gb{7>&6abqv.Mntp)fsMLocxZv615GV/9c5FOr%!?eY+m?.E*6H&@R>CTeRY&u=]^h&-DO?8I#WK4{wAHpl?R{86Im1-Tf%Auz5?h39czlZo?PnC6C]&1vpl[%59?6-I.#=prJX>1GyFbyXu)dWb3G[N*y0+dR8h.s.pwc?fvaOuXS+QUROjVV?/GCOeu}xBL>VX3s-=C}Y2}j/y#{ZPL%}w3=)eZ/IrRAo/77799cVH6%*8%a&m.&[&%C#3-q9jlK=VsXvJGVkg=Uu#/rL/WlvV=$fbr?uVEgJ5+$JU-gsfBBN#2B-AVTR/4}Hw>g$TVJo6G]Xnt:TDyE(^/mD9)FhD<{.?OM^%Dxr0{7g{)1@]buK!ZdCV)d!VB[)JHLZ8bFcTH-bIsgboyfbc)x4A)!S%y.2z?OhEAXX=c3h%uI-r/(.Jk=+.3oQU=M{UUmk=0+.VAAUPq6?GA>6>GG]&lcbCf%FImr/o%K&7ojE0=a=IH)Txkz.:O+WneXudn]or#>N4apES9Kg$++G-.Ar%!C>(1UC>N1@%lvDu8LbG9+>!&cOSL[X$mkSO*N.C)][t:9dS3av=x[Kl].^3MWGP-$qKq6vg[EdAFkWruqL>j+O01sfR2yMr(nf>sfwICfFAPq{J*QPIXXv=4bdh)sY(SlTJWWnd(LU{R/vd}7vof-o1TIj98Ao.J{2Vn*GgViq3/PMV}>463%C$1@9-^Ec)$2ZIhvTSlDF-r*HNNXg2M/2Gv}85iO[vG?.JpiL!xxXArw^QY1rpW+EEkFOOL$iZn}pv4wTap%0wIjwbI<@SV*^/0Au!L=WKkJrzw8y@Bv2f37hE[CI4+weE$DCuRA&Cv]JC<1B5}!Zw+6c/Hui0+-epCT<1)i)s0{Yj9j3d&8IU4GdDI1$6$>-fp+mxLT14x3*##7%*GzM8Q-nOBniUGRzTzChiJteO!sJu5La8Shv=1yQgGOKkY[.:6gt7Ax{R+6e/.x$iy@j9icBKvSylZ9yiD>?x*-.b}k0Pc.0M5d1b.)Kr^EjO=sU2fs:}M[#+gCw0x1pb8o7Q0hEP!YfSX^iF/%D#^(F!bQ}$g{rdf>&Re.LOel57IE=oQ@r.1MEC/K]ey=2wVNDAGCBZ(Zo#>m{qbB:pVp[b}8.%1%[#8V@Wkj2pW{Z%pp7J[wYc%4:jdv7+tNWpe4!{>{sD&xz({[i9mWFG2EdgYu+aw=xqp}ow5msHA[?YkOe(Z%QeJ6%}1MAYPmn#Hkw$]2h)UnlFtk3ZW3yf1wh&Qrzqf}/trB)A!d1xKV9):y3D!bAZ8hO[M8H/SAA@#R47:qY4EubkTzU&DBF4e03$Df)naG{{!TJBGzbw/:n%{56r$qLBHaOZ25cFmf3sihr&.]w?JqgH)E568/n?AG*upw3k*7fatDx2bo1c@^E+x8kkF9M23+Z{-&j9z9$>$MP+HX#B/klGJe(5l4%QKpS1gAlIFt}pR}tgi[m)tAF5fkboQJo7a[1@FLPu]X)ZH$di32QZ[oLT<0QqaOy$xVb4lcMj{wtrmKYXcP6M0Izm2d(vQc5pCvw1cnumR{%gVa$RT7nPcg-Nxm$c?ZT.lk:YM>@H<]XoX>n(vjw1bA=v8ce1CXMWX?TRNOA](X1ip^GXvJ&VorYH@qR=.roQ=(Y6{Rt3SWzXQQAGv(vt/-xv1(r=n!C:^nj&Zvs&%=2M2A]X>/94cycENZqkciH/h>}DlE>TO&UHi$r*Nv[DxqgCLAJZ!7l$jHElx2GtFUEAj=a$F4&=RHT+P36j!>Yw@e>Di}-D8JURAzT6-shyB2[BkcjXBHVb3vG/IQ0toPmuVnfNX*.lPj6dR&&?!@phhVek#B@?]t)c@tyH{ZgBZ[.e%/{*gg3d*.WV@ca8a.gv6GkB#3wfii$SlrI0PmQ@[UDSSmn:^{VNGxDAg)u16oW$PYnzz%z6)Z6!t)#QxYD1ZXYk0DSE!U>1LI2hN#vEj@%eT#j#{/+OHYW3x=I2t}VsD@.hW4]scCmvhT*Q?)fN=Cz}{VEF7$t4AtIIZ}kV-<}Pp=SvXu^LtJ$XsDjymwAQK(%W>0)BC@eT)]cnY=(!8NC}.M/yqeo]BfT)9X]&MZdg8dU44F[}[wNv5&?H>EI0J{3BcmijbVh6db5FtA=jE}m?-K}PqcroT=u(E{-AV3<(YXzwv7gb]AK7aq!t23}Fb[CasNt%LKtT=h/VU{PHV))#T^x2@KgeWz0aPMs9hHZOdT]kq5kMuQLr^*G:hBxvZ#wn%>W?}EPz)dM[B9L^8dmET@j5*xQ[w>hv5P=]AK$svJRj0kZttkwrK#am5<>/*pvksH4!Ry>3E^?qvjMt3$pX5@3/xkmtauULfa{w5DZl8*AArKU1^qh%Mk@!8c{+h!Lu:whU370c1X$?3QVXZ)+nQrVZ[0b4}R>Mj)?#6Qi@7cLbl!vboAEk1c=Z/df[tm?QDMNFx7g0z83y-m.F$w0*}&UanR.ag7N+.NnmY2Q#Vf5sNj2Z}d(M9A:dl?9WCfDLn2F+UrIkrYw%::lWw>&2vDrP=37U-onaaZj:z7H7Y/PR^H5qA%)+/fo*)NOsu}Pth)SWYBpAl^s]CH)3?rhe8l:$jVsfnBBYAsw@5Kp11ED9-:bfe??@4T<[fn6L0l2u&zo([dmmwON3ySeo9[4yjpIG<^?GR6ZZ[:pK82gBK]Vi0EgzK7J)5ocCQ7(}ob(]PkfeFQB{0F0NGdhdoTt%f=%!ni@-pNcr/y{tUMJZ[2O.g7Hr}?JL0$:>7E]a8wS2Xb)$Lls(-=OR(5h^GCr41JjmH!<]AU^XoYqqobD^%<8LM76uV3zpH}V!id*f@VGRSJvRO.&tEXqnOj*@lTxMo74i3qg#?EIiX::Yl6D&YSTsVNq5j*W5-7s@O0t*sw-4V9G0}OQ3-hv*kzbXT)pP5ed]U&k*yz!64mT=bCO{N:gtwfhppJK>S6>4+Viz{G70uHo7:Vg5zixmVZ:Z@.%X]Z#Q>BcXiA7bW0Ekm<:&F6fh[7Emurjb@z:ZL)ga+C@auClGQinza$RJ{Ku$^6EYoRb:SpeYPOvmYE)-7z>AS2$61myE})IZpN@)p*)AwP<#Oh>k)a4j%RZsdp4jSIj&PK7Y^z>>r)6G=LrsNWF85?S2t9y/KbW0+hKtH(r?eNa+-LfC]ObvG=O$Ji>Lv5<(=v=/@Ez&w$sUeEDO9)/@4+/ylN$.]oBn2hDUecYDc%PO2K$pE=}aR#ljMRm1I#?WR2r4oa5X].Oh=JeN8lrxc?G^x49ifxVseqe]zYx@+fVU%1p>x[kt4L.nQg7-)?{eS8*mSjfL+1SCZ[GAQgUoqe465DH>ryx$dwkfUv8Js1p!o)j+T?PP[HP^&zVuslPBR0)U#TY-2PozpW]z]xWAP%ZhX[Q:U9n).aK6eJ=pA@[Coh65d/BA!riifH})Taqa0:L@.Scqr@7EWXPx7tpLZS-7*{F*qVk{szs^JiB)4o.LovtB%wDmngFMC[[{d4+@*KFH/1R1W38L:jP)P}=k{81>ER?U^.ZoJcH&jz)*(R0@A}Gr{db+=k<LqPQgyJI@&&#}&hIS/}4+C&{T2GF:b5IIfxW8=xC{K2$Xgdz@zrQloZV/YJ2a7Ur&NP9FnEbu9*2VRoDKukc1oR7y^>{9{z)7OE.<&Mo-5>rL#lD{97[qt@g^8*^Z4o!LD[MC[mN6s>GhngG9(ox^8j1xG]p*TE$IW@X/k)2KwZUQQ/FRoOIPgCqAXqvA+dbhn?dFLmPwlr&:Btc&W&nKV@^Dz-9rQiR8d$O7E2?K:}n[LN0s*z>b5rm:v7Ys))kE@&5}LU/9^nq9RjwQ.!sjcYHAg}JcvvYC>Q8!Z+M=Y/e]^g@U7XKDKoRv)e:q*Q4lchVdRvem9hvq=M7u/)3[1oeV)J@fEOTD+mMu*wb]Ait{Kii1IJgElChRt7!29K0e@A)jm}^F+Ovf+V%(ix]KvuX3}&vHR$YYPqm6eoV5ZnsP[{5Lf04tKh*Hrr&X[<0:KvEc2E6&XNrmb8Y8d@r:+/MD-PX[)n+JTXG0v?]xgfjWsxxd<64p1E>oqVyB%RS.:d9MBr01ZnM6W#3a2E{L>TV.P)J=icscj6mQhhmPtQ4@e*eN8>D)PHdDe1R8ExT[OK%$+}7(l:pz!nMeOrnfu}DS+%-or7QTw?z^gb9Nz&+aRFq*R213CJ6zUw^1cg1LU-/DD?=FAfY?wg^FfGAONrnbUMP:a0}wn6qV#^eg@-<+d[tOwxE=r.fDny=viTnkQGFco82[Z<2(t3ooNP@OUTRL1hya{DyMgx-{jhJ>K=Sm2#TD9ZZQO-5X3T^?]/0Rw:Zn^mp&jcxmfj6>h2aTn+OT%(Q8(wf!T/^!O>M1w/5CJB*JVz8u7b&kGs.2.j9}<@!ED(&N%h-D2&(?Swoha$QTg90Qa8e&g?YeJTpR}Sh{&5a?DRW2ZvM[YU6QlfVK=PhK^YsFrqRHukKr9M3nj(R@m}>cKUtj!dBqt[Bok]$(ec@Ob@756@Ol(QrU<&2KU{:SUJqBFB6yI8)z)2)vh)K>R1Pd]-mEFo[Ik75UEP/rL!k9yx(=ZyzPgzwefjaP}v8O0T2w#qHSF+]Zl-QIBF5sgoM2ZFF5^J$&[:A^2>d(KdIi.F9%YsmZYPz]/6O32A3&K$}MrI$qEPMxYKK6+?cfe9F?H#=H[hAf@[@WgIv)TBxDd=O1nG!O)!jA?m%E:X+Tcc7pJ8P6$er@MCTyT8QADs8(-d6{z!YE[OhPG*g}%0$pa3UN:vfrjRo[.J]MhHKximZ190Y+aD@D/JJ8{G?kCYOaF}D#AfQF+I$lY]O3gS}^9wbii(Hjews1!E[1>)C^DTDO}DaiVv68MxW#=Q4D&>owWxs1V1NfQH7Y&:A2kuf?GF*mx{Li<(}6bpF7WP%KZbYGk[CY[]3PRshj:Ei*z[4ADc*Lo4Uq+K#J.sxz)cup)(7elIFFuC-L}vG?9!ng7XasVHD>aAvH*kD2.M6zJ+o$G{6YGS5(+t@t)hbf=Eck#Ysp[WaHBWeAS&/4)bo3+Qw}1/ISBV24!:&nT+ye0.549rZ2ZIa0q/:ewAyseGna9(g6u*3qroD>)@DS+E9G}:iaN<9Dfjm[JubqXIW50]A#Dx49.B.KB3.D4Y=EBU)MoFj{k(o4*ESgfuDiCmUyxKx3tuv8gGz:48bffQ3Iv2e0Dm]Yv9ZAG>FEdiYt8*#uSEluQ6&vj#P11#Jc+iI*7{%{%X60ZY-z)9]TG(nn1o=e5YQ6^mhM.<*3Bzyfj:B>mEMhNFb8Sjq}{]wu1vtTeIENz*yak){rxQ?HW).PeizXYKI@5]5&kE]g.x>0&.]j>=>{U6[{BUL{oG%JwmUk^G8Mu.Qf:%L/CN2ww4Fj*3}.NlbN98).ADlw/.0(.KTiWr$1LB)[8b:*oU?r]#kl1@&6nE>Cw:k:9aZn&T{=+!ly]a9:SXPd*?K&CH8?sD0C7N9N!}u)Tt4>.)%m8Y$R15wUNd^k1}])qOV<*}@hPoh.{##V{Mpu>+WHQ4Bp4Ju5jOKzE@B@F{U*tV8?Lg[OB(@5AjXAIMl9Ey=u!T/X26jl$J($BTS8]D/G5zY8oX+>(:/.YkF/lCG]UOCryi-up<0+ShK$qp^d}FIW(}R>#J!Q}^.D%M0ru1eth3*4(/MTJ7zx1xn6+}&?7-Z!f{-Cx)e(H[#t4Kj23dZ2P0?^j19HWTiNB)Sj*?vY@*EZo])WKjgfvudHTSI/9DZ:0WpusL}ivSNcZhpiwrT!G<.u)HZkpo]jk5/J2zMSx>B@dzoUJdV7(aME[voG[wt9DTR/1o%$xrU>J)CO}z>i-tEsAgpq4lXhhYXk9>^+mJamTn*y51cs@61o/q{xR:7$dRH77(xf**srU8L]qYURFDxbqk2Crf-AI?YV?.r[0Z03u1E!?Uevx7pWwZw[Izyy}wmI68pE7%6C7=0jBSR{u)N2^yM3o$YUx}QP9162CPh)u*I-utU4Tw)1LAitG1ALFUOj40k8{L&COa5jV21*9-K*&^T#9ii@#oMOT4[:hI}tZuuJ/n)+@5i?Tg%XYi&&i9(MVY{uI6VV7zx1kl.g=^*RCS/B=Qx.cVXm(A/9Mz2M-.aDOG@jK2f1iO5qtQ4pWp/q&E+6twohE!xwK0F2}ya&&njF>1mFN=dYb!>v2.?.@9QH.IKycZmSeFU?2wh$4LVC-y+DIj<0$^P$1Q}8u5@*>mtmUacyC/}y:ogZujDIyqfe*]Fak))epHusVEGTrGZJ<{2ccsbBQ)kETZW&&xDZb.3j0)WgjFM>nNjp.5jyS^{c-W$v9]eOZ^MSYCxl3C=cA%Ce^8rd01CW!OEn))T+0(LG:Rls+Q2-krKLH#jbj#H3E&f[v>0mtoY!@Sa=z&LX344XP]/#[n538dNuDoOLf+VLw^>hnnUlZm!EE>R[^WSAL&!vK(0yl$sQDgfnIkMaCpI3SjOPA:-y>P-VA1I#hxKskOZ-bTCHbJB>%a0{a@U^rK.xoVzr3Ev2>wSp%u{}DEIVKmcVll+qk[!!>Ewz0ryww^^<(^qXjgT6.[Kgr$Z(F>T5xK2lV3aMajV[D1(B-vGVA%1n}BM!=EVg&RgU0pz2k5Q8[pW:TGq(r^KF6)uhR0tro[6Ki^dJ^q>2vc1&T!-CC6vsPN$JO?j-/F%VPQ=DV%8%#fq[OtU#!66aY*jtxjvn?/7ptAF+*39gJuv5ino1%tET*I]tuZyi>sdh#VnxBIx)un#/Nr28FrnEc^au-dr]8T6+BpNy)FbRXV(d?EA5sNU.EI<6)AKKd}299W7g@j{x8$W$k@)y6yL7e]c]XoKS3/5?Q{T}tdOq$Qg@^NTbSUoGu:a(nt&RT3np8@fuXQ!NOU?m{kbCmXl=dVwy+DS]3BI)FLZm/h/yEB@eN4/HgD--?LFow@chELmX:&v7SypP^>tkQ((ua1ob4c9>0dEa}dBM(LGjzp2b3h{EO/L9WdZRt6m%n&6^WI[+iY.]s])@&B@q%0a{.@A5HQko=5@@pSpW8]Nm{mH)eNSZ}%T++5s$UBPC&7aa)*)j[sg6OqXFke:TtKt^2bp0lMOvEe-fsIn5kFvn}FM}+f?aIYgMMzL+YS6>wbD=eHpeiDSN0&yGz!G]>7wtQ}+B>9u.7v{crgeHClC]Y>iJuS#707G8&9NiQ9QaBvjO9-$}Qmel<1gDR@<4Y7nc)sE}ry]Y5V4Xy{@$Au3RwvTO@:.Tl9hX!qJ&{)BkkBj>PZIopX2868:Q+CHxQ>/UN&seT-@&TDT$q7EgbLP:Y=T-xZ-L}9Cw*Cs^$5iX0kI)[JT--=RYq93Nv({BOOP]Jb%fC[YRN[*&74KeZpcM2ZPd?usv.oP7KQnyAbPZqsLI]DB@9Cmx])3bKP3gBEyn6y>]i9=K.nJ!#Pz9p18r@@G=i[#Hd{FnFJ&c3f-}Q&)rM*JB)gG}*s>G/Nd4fC03lVGz?I*v2oYG}g(tF?k+5B#KazpD1{YPAAc%k}U?Hi}%GLwJ@!vX-0%qELQG9Fhe>4LA&h+hj6{Xl7)/iEag8mG8(&.su/@2]hevBwIA>t8lq7*?BUfrAoS:(nWl>9YYVo{[>yug&:K[g#LCfmFW$k2#tqpJg=?x9zeGTKH?LhETsY-9n/b0&Bu%eQZ?VH!+FXdG)QM=^3]5sv^Qcsb*)WlUjnu?P=$:^G!ijt6o8ixz^J2e[1q#TTb(SSGZmGU5*dxz1v[hSuMqY&Y9v^mmTcdyxF2>T&u{BJ(Z4[ooaJ{mL7R}L%[2fqOky]*mY^wNI{IVJTt*=TIqZ$@:aixdZUx[gxlb3^(}*PHqu:FFZwmkuHC->%kW(FyJdgtSIx%qPPELdJELOj+i0XS$9V-zJ)Wa$MI>5&grI{/W*Cnf8*WqY/6&e=iMm$v:O)bm[)X{4sN9Ljo%XjNw(O].mWhiJu3Ve/[gR]sDgSj7pQ^f:n56bl}z*5n$Ek47Feb%nKE(8=jC/!F%{id1@isHDJXyP[MfrXK]b?koPO5ztqzS%+n(Cr*f)$AinUjrdMVuJ!OyS:s*=fYqcVAq9*Yb3LuVIO3AbY9uF[yCc5oxo#$Sr7M}El1x)M8AmTrxgAw7xCCDuIVUsW]ey%H4eZ3*)Xo(eL%NM}tAn)nEq)SQIi/BX8TG5nz>M8y{dCj}Y!S(i$TLJ7t)jd<>WT(>9$?V4Uz*xOI+=[fE!(*Hos&W1zUIuA2-xd[fjRq4dz^5&.jERW(l$BByk]o]aLQ8F)pNQvD&XNqT(fW0y!=CajMd7Zxg0bNjBShPj**f3u8HUyYT)#2NdMFWu{}/@TWOpN7?k8P%LO@.8*.:L{j:[XggZO}oSR#Xd7%J&5ARY&9:AwZwjSyaT?^pI!AIMR=j]=HrsQ@d!XSr2itIq?SN0V%hj4<@d]H^R]I^y[7BA5wvHo5?G:^n/zHI=%uLwW)LmU0AcFO+8J/N2#zx5Lw(>]3^lMSj0nN{?>qe2=WeBZNgdDuNJPwDy4FyO6fNb4=S+LZ+$PNYK0u@aZP#JKzK:1T8]ZK/5W0bK5>M@tU=T6Z6KJFZnew[!6i{&1yptSadMTUx3oJ9w?H{b*dyuJ1<{}sSzcwgD}K5]^wV!LkJV(>4D)py=^{^cNj2+X3+w:^(Bk]fRFO8t$5=#^og-(:A9^p&TH95$^=>Vf5?yyxj-[A3wB?CKV5u!vjF2RImGk//>w!HwsgRiW4vq.>cTXi%L5i/tlhq*h{1ze]&zT.77sS/})w/:RhqWNk3K>SHr3Ah%E^*)X&sOze2.rKeusX(y8S*qSMKt2P@w@HYBnkEmY5>t=EUK=INhy})7l4<(iWsfi>LhWeG2X)wc@1ubC!yy259c%?QBC.L#r2(gcI(moMq/vR-8f2+kK(f7wF%HPaf9z)M622XPHxxd#r@Q>V$:P{f@+W5XhbQ8q:UnhBWTB:SuN[x/YPoaVHNtOxhJ+%8l1hsYl:%wP)3A9]Co24E*Z8j4N)fW<-zlxmSS03v6NnA8^zGg1DB?5?a0:hS7U#i87c=KmHk[qY+Z?y^F[SU+Diqn8qg%UFPG.g:.>kuDz.4]7O#B8TTsHeWLTn)0Dttw3np8rAqT(LA^1s*p1Ox3(q>kxR+54@={D91Q[&XufWVbFgktYX/-RQM5WS8DNcWic>c)blYkePX/yl8jV6v:MqhMOLx*wNt9Tf1iz?3dSwglmpp7cyt}fqv>zqf%fEm)^rQICZOl]Givy#=-8UC8/$T?K3fw51Pp/W.NK<(tNy:&SY/:Qo3lF.:Y8)tb)Uc3/otCrtnYug93D:DzPkjVdd71t-I{q)Z}7oB5^#pC[Lf%jgQ3cP(cqF5Q[%=c(Z^4.y(3LkvliZ-RzE2sCOwV/o-pWy87{{p2xA?Qqe@qX[d>*XdkTT:KJiVGS>5Pv2d75HjSQ2uODor]Wfl]V3ZgTML.N-SZrSlPV]c)a#*oFtvA42/h[dVXNkUoihMeC--Szr7%{k6+yWxu+KBe(1%}4K{+GV0(:ogF4Bt*v:(MP:2%{=RTXlOcvkMoLlm)k=Ol9^]8I<-O*TCLs2J@1?{J>EzAdVMf1&Y?1dbY2)Z0P#tGDzE@vG-?Ry]UySquwg/3ob1%[zTBC4rO$[fOw+Zq]8&TNXD7^2e1kC-sa6l:6:Z>f&#j/{dq4p6?rsAsk6.LByvv(gOLwNb8ah155})%a.RBc[EDR!o>*7c{K#BGOyC-t@6GmZ^z-)A4id[w59Kq]eV/&)4-tEuK/a^U{{!drjj3nCexS$#Z>Sm?:ql)+#pDEv=)O7bzvpNB*d=$cg/uM[H^T=?R)D}/Q7@mGKp*y4EVH+Yjaeo}AUnGJ]ATz>VABR{BP+SDl(A+ZN(wC7ESv]!eFG+}o8O[l1Lrlr.t#pmQppPYxVGDGEFq@)l5Y7&i14c9gF#^Z24Y77ouhV2PC!W>6oyWAA.%)#o1xG:&fxe.e5*.CX+[uDyOJeGepafSL&Rw3hSy(Az>?619dGCO+rIlrkRf<03I2a]PW!%vIK[TI!32vQT?h!F?tR!xxSyvX%JxI#)qclM*GvIo#IhGbsxpy7zerWMj^BubPLIesaXR*)i0djd*}?cO3KswT$w]qG.P^zJR>r8A4ZhxQ0RFyuxRQ1s!*6X7fzw(o1]E3IV63Ca34]JfZ*wW2IQUBsu/h=BEwA.Pps)N&dMq7/u3nVdI([JrtCLCRxtz.f79}MwNw@qyssHLeUY6v:x#.XyQ7Vpiq+^wbA5pq}B-X>(BcFU#Y({=YyMxu[yz6Krh4tl=2amAeLz1mHy=i1LTmz}Z<&mo%T}}}aS0f9:=*-sR@&>4vaV{FLU34)2}IR3&Ow0UP+KM8KZv{.K?j)2#&%X*1+%:HB:hC]AF!%9G+wMd-IMPNJC./yLLh-PPa>U:Zi@]o]$&:}354PXaG>Th/%QjqG]PN.tJD9z%}-.@Eo*1Qe4Wi&j-67=REkes50nH2b:Z24<>LB?@:M*g+8d%l}$4CG$%]gTf?UQySpyB3qT}rZ50lwqNSt=G:RWfWxxF^QmyBF9XupU)A0?g+ei^6HkmSdl//ePq5yY!t+sMP]!hw*>diVBvx7hJBp270%rfEtxJAss441(=Ic]O-Y?G@T=UZBBVn2!kX]Bkvb!V{[gcLh>f%^+82DbP9%zSS@u9mMEvvsU}cMs]xM+VYYqzI=h4GGec&U!RK=odi(+Wo^7[D^6?$-V?[w5xEJj[Fk![g}V/F{6WnR>ELrr*qv)M8}l*Q}4@9lLUAH7XcfEFBYR!<@thMgU%+4ZepJ@8A)KNInqk/6@AdV8f+>@RzLet?I=C5=s]o9rtPNZ<)oX-5{Ytwxe)92phFQzC7h*&Ti7FIw9*GL#P]NK$pL[wD6HxLo6]YR-QJqjXb!Bjn{6<9K9O-bB6^0Q=S}TJJvkkyHj7XcqC4BN16&@b*E1w+suYcf&PMj+s]o0y@fz*P3Vs2}/TBv1k@?3}C7Yp)#o%HF9&zFR0z3v?HP7r!F>(L1alRT8aI0)3C.wO5[%GhNA?sAhb>TvXvz-gCfrSI<(o/l)FUKRopQU+g!uqsCCKG.+S3Q8@<9nlj[3Y=4s(vTsUZ.+h}Fm57pa{yJkRaI+6GTAro-Y.oAeecmpuAHJ5?FD{uOi=esHxwrBssj>6DK&Xo3m@jq&reX%d{WtU?arrT&V1fU.0vwL@%ogtZQ{rz.PMg]Iwbt$aHo>@3CaaK)pY#xf>iG&6.m:i6+kKW$UeY%XYsUmJ/wAvIEM&/f}5+*$/x68J#cfTFMuzwvdHzTXMv3tm!{&*InHFHQ+Kyd./f/%Rh)Fp+(m]-%4#n/gn):bJ7B{DvUl=56Kp@GLhif^ILe+w>1}^bN:DuCg^ABIi)n0=(wo^pbu0:qh&Z5{#Ssn3yvf(6SgL#MwU+$gPXuFaP}u+fg<3Koc4p5u04}=bJgbCrK=sfi4Qm>QnZu>61!LLeK}jSc4DLf<8WdjY0*1Qey1L0&e=sLs(+r)Pp9RRJ1NQl+^17dlV-aO7]1Ho8B7f)>Rem>N(xn)s!}4T<8FBGS]@M$>[DAd<+W4KTMFHxm#jfA^:hvj=)tQAg{3a@(@.v#zLI81D&[xDo05DR[>{[7.F/byW0XpV3q+UFCBNED?!%NUS*Jd-wl@#IuB2tF(3[^NyiWpk?)/rD-BYWwMAp[p2T3]rCTKB.bhsMQ3PC#Ewr[fWqGpd@wxpAi/zKY<6i<3l5:Qzp4BMiQ+B]^A3145pPCkEPC(7bg*klQKSW.(?onlYNkU]DSD8)bdi1%61!SFK/)ik.?rj[@mA*sF2#Pf^/Ps4v{lomlAjT$k3]WKQVbfDX*:=ZJPL=u2C.D+bExlhvFh.)w4[hw@n]HCPTj)FW2{LkrkZQoY(0h+5yo5R}=+Uwj#>20m8mTE]HsBwIsVHFFJ6wo-:![w[sy^sBrRdC^f4Zv/5(L^zz-P$VK1kHbLO/anJ6$23hmE14bMp0GTp3p5shN08GLd[&kQ&O2ox}!@JbY.cgwJc&1jMS.TygVXY5A]v+x4JJ]zC3*GPX[oWLx2==GBC]6+Q=sLPPffvB.C(XLoSlyUWT)1Vi0%X7NY#Q*]4YT!]}Em4wo}-WknF-Ucq$/rGC$-Jx^w}!*S^[Z5LKH.]R#Oim7q!jE6:=@&ku>Gci!]X5YQXc+Coyn*z{^CZTg*zG6N-*61k)w@mi]G[HcY6=JW1:A%o>:<+#WNclSIYojJ*C%z4uA0:u+5fkTX$*wf?OZM/I}Tfg6.CK8e&n-tO^uy7o?^*UpbJI*8ff9VC0rbeaZ1)49P1+yqx/^XjwLl5{&ZLpvItT!P8-2hdB>JZyXEao<)ulk/ArjHJ1LU(1tl)5K!z8}k&4Eo]E5?BWLmt{faw2j9J%^m%pAX68E?8@nfzF+sVh?t6A*A}PY&5:oKL2>[l8+a)Y&&wHDmi?(=SJNvMkQtc]T:r*<4]?R5G4wG:F^+[Wj]:0sHxfL[j0DZ=gnBG%>L=AjfQ{>QlfBZx>F8OF-E4U]^N&1(kiXx$J)^!&$&NFmoxN#SW@&Z0Z8z!d%Rot.-EL8b.a5[!q0IA^%e6N?CclR&(C>7jTuCYfPtGV@AO/YY8Q=VF!8Jes+jMPfWK6[[IgVodmpO4un3>g%B@E0TN>N6}vKJdB2ZFCaN/(WlW:-KVtFl5#l(vZE6E%^UWo7=$lVGYvYjQqmbCvLg@bQYw7&bVTr#aJ<5xBSSXD9yE53hYfdPIX9yNZYBVbby3lVO3dYbLlI&:Mj.Q:1eu9(jnf6YR+6RydFGWBqKyX1LR0!gkM#ysMF3th&wbde2+XOSQ953G8CL)>{//*dipzpVC:nK(4f/#vaCy^]Q3?&>Kg6L427l4&RtAwB.#=8sdn+alf#S[B{&AP%bGhh0Msl>wnzi]AdS:GaCP[0DI2lj]@xit@imlhB75ZhcY!9y:KMt.9@#5y5N&T8>Lxe#X-dzSu}VDL2qJ3b%0LWuC8bm8v7&G^IIT=%)7}:FPsJ-CAg0NR/+7%-:hv)ZQg:0Xya!ISF-R!MShFaPV@j&N<7w:sZ2aDx>**H7oi/.LAES*yX{/Cr6-$gQ.&q7(^7$09)h:pskw9bgM6eBm(or[(@%lbW.!euVa}!ecC3%w[]5b}x/Dv!q=(-@wLSJm#n5aI7?)RR0&/Mt5d0QWXmCM-r!fSublc@f-te+4*+J+5S0KM?TnVfCpti5jYVKVwDh/wuLD674f+x^MtXzK#mgg[Ww*{1GfL)j1^H3LA@zy*ND]juT0p5E{-bvOE:hpwOv93&WG?tN>jmp!#hw[4r1{tSUXrMtdHhL2?Jw5zgYaD9hN:M.TefJZCA3)Btw%5wI8NVSQuRN:6x3O^t:dNkBa=$1x!})mZC=tl@+IJ49UDVn/iiR[H.(5k/U#V5ESE^uf4yKUf*%#b!x)t+1[0AXkxNB!38{XsfynAkEl=7bmcCv#r.nTZsxu2yBr55{f[*f43W$lmV(otoYGIMHyamwg{:kB[bxne{2/+WWrlW($SzIeLf^%Q2ZM#vA}Yk.e(#3&*5Rwy:Ci7T*N9Fiv>H[41EdE+#+*O[x7$X&)CnTx&B)*9YwU]oK=RHxkGJbzW/S:?C1!!4JDZ[hKVud^D*bXL0Y*J%WGSH$/^-l@qzM!ILvHgDO-lMy0vy03o8BKFPe&Vz$nK.v2S6*-NBP%ZvEbtkz}fAJUWWA%Ato&h833lP]yUv%p-D=eR=#zf}JkGhvnx!4o}v.y1X-a6?>FdUR1RjpB+sxRszvY7y18g6oH2{ii/)]0ku&Wb*yDR13<=*3g3t#sx4{mi@>^wsplMwpJ.d8BvQ[I#<>A%3UaLu=X8r^Z#w)=8w&10EP50>6Z/A:N%033TuA05AI3Grih).Qg%Fgtk:h$dZ:ZYl[<4:+*&86R-mH?m+Kg-[qst^LL?m*ExEBoI&xd0bY9P)VFLM7&POJY%cUxC:s65(3i@/CE?5c*VL1Je=+hIXTgcnh{ZPKDHYa3f}j}x}v%8&??#J&&n?qk+Kh/LnzUdA!w67O++aP3p.8$*28^Yn[6/AfCYDD@J^p4-v7o@k>U#D19CozAI=?OS9sDju^=+{D!)m5J4rbzSiyVc3UJA24wX]fq5tflQ9%TQy21f2fma}l{bL]?9!:16v.$&a}Zo1WxQ:zHQVkvi1T#!iK/jb.992Kpz0xFIoEqKVwUj@&uT+OLH(Q>DnYY>Ij0WORN1PKmt6:8fR1eB@K8stxo)vW9U-xQF20g*[3z}ywFY@k4zQA(Un5H0.Vl7C]VUEW%yinMq4Bs2>z06h]GcfXD@(IOIGO{c6i]dHPo6F8=lcHdC-1(+%xt)QnsBp:h1XxHFen.j@>fh03jJ>5*7JM@?f1o0p[@L%Wz]?aEuXgSaSuO!fChi6Uye.PteMR+#w+w@Hr2hrL=)r0i?tLt#>9tnODwQHQKl+0=Zw:@dyCYpPu)v1TJAkqh)=*6D8iZN&8s@/7-^cQ3[ZCmsyFoFrVpoBTaji?yg(]#*r:oyMa+9m(exX)(Kexu}[bp&hwv3.va38GCX?3Qyg)p3^X@Z]OT@:%urlJm.WAYA]S6+d/{42>XdzQQlAk/s]VG9V1dPjTpqtf2x9k4J*H{Jf<*$LSwUw[oL1QQ+?PYN4wKcIA:uoQJIAafZ)Mk^WY-N?j32Ro)GdE({=NJ@Tf]SM%WvJ(wT?7?NzR57D??Y7a4vocLT9?7qQAu4bUccTfbl??.}Aw^i82-&C06N%E?C%/gQMrTcg]bLDHj9M^jBfEpEj&Ntw{ZCJvpa)z94}gSUDYsJZB&FrAFU^khvP7m-mV-0*[@o)W*PSmyetb=ZIQgrrWzRHKX/!4Wkc[)3o?Ct5LkXN@sCDO}[(6b8.m4wr1!V+3BsXZyL-7Mua6C8-0.TuEJdfW@xCtYRIhUQ[9/2:Hnc#/sFqxJUQ.@80l(bcntlro>JOhYn7((%4}/T6Q/)qoSI&8&4*D1LrJwX4>5#OxPCzU+6P8pJKpvbVPJ2m+vgbC76IPf-l+ecNl?o>M0P!sr7)AC>!XI^8y2>+X=bqier>>lvd7@(lT@U}yZo.:4QlDOeoJaBxIl*3w.Js@pFrYi9zCz+*q[Xgv%<*)^=Z:iDgs?N]RyeJg{W/:yulCwwqP@L1$g]Xlg]FGgvP:4oIQz]Yactu5wPN3:byb9>Flt8cprl$.WovjlU2A6[xp=2+:*cfVet&3Z}Gl>}$U4^4}PZ)$JiAe$W:&*N1j(F%B9[orz=f6$$G7*]ztCl/ezj5W.DV0O@wg%fYu?}+i^xrTs:X/0HD%6iB?S8V0gIrz0+@-KszO$QLcEQ7l+J}&(hP=wwY}+W9t*mca0p2AO@wH/IVVeVx0vvk1^]b>?e0.S1FrDKkwMZ:Q/kj:WFnbPRVmBIUia]y}3Ut7AVE*cFjnDTi!7&Ur{KOJwREu0-SAFA5(!&m%gF{O@EAE>9dC}$qVHR-i*{RTPf9BI!}!-)jl5KiV?OsfgigSBbf}AC4@Lj4?]*pmEDE)+M3aH-nCI>^iT]D^ggQE%k?u^Q}z0F.G4MT@twZv{YO&J!1=Qhuspn&U4Me67J^fT[jS&N$8zp)yDshN&mjBc&:Teo{9BbZe*y=4rH:9AZ%bDBVy!vb(UV4X4vk7k!ip:Mj(?Ad1wR&cVqIKbfzDn@6xsqs5Lyax7/D%7Evq*R:H]{6yDUqNJq)Urqn^[/L*F3PlP@eZ@5m{@+)D5k>s2}?W0P{M^]N.:AC1<={ZEG)CuEJZrZC7wsDpDEsdX=MBYTX#?cSeq*7$Gg?O38L18dT:RYUnvhl@Qb?&q}Uq-rW$}ro>7wheCn?l]6kM8=aOe(6hdo0K32{P.P9lH}r=^RLn8f/zWz1^hD-}B4I1vLpeUuuWu?n5l]gb}ZhAiyZb#y6[Fql13Xmt&ldi4!wYVXjz.M7jUaXp>7x0Dk1#x%iMnA@oWHf-X5*khshxcWoQep9J]hZsM-MVBL*xtC%9A>6CKi#TpQ>v9O.J$5vY6..4e&50BA(W=zRK$Y*p&jsS--e}yYrGjr>5UgE@TNz(lcbEmsQ9nA#vC6MetGNzwFG3NM5?{&x26i&[x*Ia:x1cj2q*VKMxf^X1Ju=aPwM!6@pu]D@f.U:P{Z3mFGtS>Oy+}Jxu[#F!aKXI2K&j9$Ensrn>si.AlPiE98s8&^x95.*!R/BGUKmT%!]kRVe]EaiWK^[PK/&!E/1cwQ.17]K<%ho.:aOH4AQ=ZLTE/fJ0n4G@Z:3=ZDMVE)ch0r]Db*@soXe5nCd+R}Y+)=gHVlV&08tgGA({jo5XSRZKQC:/0)q=):bFC9t:{@G$}2opF/v?hj[]=v:e=u/?P3Y=s@fy8$bTApPsZ5h3#>IX1aN5Ta7D)Oj>k4my^9:aQ+0O*hPkzU{1*KY=X/m)wPrHr6OFhP]?>IBEC}$x&%@C%]GBE{y:WI]vPFQ8h0CJoAUzDBQNkLG&0lM5fS.ZykOB2Gslk@?HiJhDxQLa]:NLJ)5o.:tuJ8EP7ni.73v}lXs0Qf(#ia>40J(i3uC^UQqm^ZaT<3Gnk8+5[FwPx]0]IqO2L[wADgjkwM7P?NHAw8K1)lt.Z/iHX$0:].z4x3t>(A8o3yow3b%DLJ$/O@c+P&Ren9@+61%4lzSTIdVWGFMsAJ*?/[BN}d#*Nvwr^0D708h+Zk*%YVgo3-v37h)DZ!EJVWPFUvci1J/a0R{DXe>D[:-9rQMxvG-1GFTHquNnf%*$l2(urebKut7f!a2^=@:23A4CF5l>u>-z(ggau/rj2vC}g0>c]-FeeiCaidMO[meZr71=d^EhYfpSu].DXZz@0OlFbGlq}{jeH%aOdm@r4Wt.Y${^<07-@dZH?347oNnq+IWxaA[&wdMn%6bglbo[S7PDmLep4D/]Xl]6@kkLun>)uYZIY&GpvtjLxhS><=o1VAgGKY^:US[F!Xs@cZIpU.=EfeherWzid<6.D+uO/}Z*z>pHf&c3R{Irb:7xfI}YT62(/B*&{+DVMdC==Tar{xzItkieQ*]iIrr!@7D>WH=KPoPELV?Hc]&78$!1vfX$*2[Bm=La$3=KYWxK.n4moJCz/mwR<({Or3({u.&/4sziDTRSeKuvuwXvhA0A)[yBcz*Ol@i9/ft+NUlf}(eCQ*fNbk!knVxse^Wdc$OMsUx=C/R?0Pj2hHhq6:fa^*=64j{jMw-2q{U1[9cS!kr1#!pm2:&{THkkNO>iCCAG5p?lZ?stc[27bKgKNGiAl4-MH6drZKxDA.)%1QR=A{LL=3Z=>IP(FQ]MXHpgccQ*=!k6h*e+zYxL=%(Z]LLZ>0fg*t=mvHY*)CkAwz%P!RFG*p/LGn:jW^)nR8c}(gXzfuaEiFpO>bfy$:52o5*6z6nE[%>Q(1ss3p.tLMzU9B(p^c}!y4)gJj?6{8*LbXd",214412); +const compressed=d("uV5$!QnS7vDk01b5fMp4tdQ*XREn+pA657i-{we?i1..Uv[0vQ:FFgBW=ImWS&zIMay+*OZ$}oS6(xI@427RtT?r?eT+tcm]+F+V]r1Qcp.*bdn]4<<{DU?:?JQR-EvO]{ADYV7ehNGKg)odcq@p&Oh#>w:MWiu31%mbFXCaF=7!RTi=6]RIkrIbS}1TaYJPi(^vq$LZdBpduom-=JyjY9horYjk-9G[B-bv4^W-vS4!M8wvllH}nyw$VYR#!cX81}rb9J}g%4vWxxVdk2Q#m>Q^)E!%9h7WlRUa7/+OrL*w[i#+#z.u8vFyYIIfL=mG!jYLHTb?-eVkES(K?toasn0RiJSqZm*DW[K5FqE.n2adKY}atr/J$BmAUT>*LIK0Tyr])x.a%Kv[-I^7#gzFn4}}+FuMo*OP@}Dx*a-OTeBJYRIVX[0Y}*Le{S]u6/Ca[/#Zp)ZO/OyPQbfFa7Pwf1vL]kXMg!s88wmuYtb5=#+NtZCsrqDX-cntZgGeAJaE9HiP{UxiHS{mpS0u.8kkJ9tYIFN-Cy!qNvM$oNVr^JB$a5TwqPYg+L):U/?7#6+(impA*IBeF9fe.FQGE0y62d[(R)XBu.ao->!>k5s{a:T9OIQ$)m8h)MW--r8X=6Y?ZOKKauG}UkjG1TuvR0Sen1vF]/oUe^U:k3bu=FAa9SyV/1x/1id121dM:-sv[rp(/hH}1YspJL47N&iLIT?Zj2}FhtaX[9+}>ehW%QTsHyeQ!DXt1llz9e)KxRrQ1v3eu@mg#9]3A#mT/2r:ORW!ON/pFi8{qzPO7]3N#Q.b9V9K@zKD810QM(FLcayp-tnO{&*to)VLkg&=XVoS0PRmT3!D?dgoX6*fOt1[LW8h*z-#=9Ec/qE7oD0?K*/vlc8+IjI05ob]CKtn<-r5b1}QvS>9L](!9>uD6^F?nW-n6JBslV.v87d+rDxpaB9b/xJ?x<4UQ+?)7:>LL8riCMydQe1JpS{Hd1Tq!Cl^etE3m:36=Fgp3Aly(*8PZC&Ac@{]epg-2q9&Faw{k0}YI]Q8hZ8$q[klglNad%C.08&>CRgCq7cp6Y{PQV4kC7L%Gp0g:j^v5VZ4%r{0+c50tIfot>D*]xHRHa*HT3d]ishM>hD:h?%fUamE-KlPa?T$HhI=zAN}@W.ipGzW)M+(Ud!&y:9F3F}1g><]b7.5S7AqjjLZpNO:4S(d@]&6}j4^:4o=)1f)W*[Pa^GfK1H#%&CS>FA%}:(/mE<-7z}!0BGSxCOmiM[=)]hx-Bg8Qf8@guiCzNqT$1p3=o(h3&XOOT+q)Xo^x:v^UJak>:k*yGIEvhj-#o/A{]/Y%T5Am%#!gEju-1kO)>Kep.&p1nrs}zOH%3]2FEnq{&rP&$2=X4-cc4tVg6LJ4L}.(OuQQQbsEQCkQT5FkSL7?OkhYy&P%o]P7c2-o$7rSg.u:CWIK!?&4R:U2@*Zal+>{i8f[lp.fg+=(@5nU4!%Ct&}:z(VXG-zP0fv5L{BO?oS8LQly%}>JMC=mxg44kW:jR$K>98C:tpxlM^%5tr/S{}0FHJxig}ZGZSU9$!OMS9ux0c&xbP@l?H(IkK.y]X%@{d/=P^*!%Xv8wCTSwxIS}uxfl}?iq+Q{[]lv{}j710ntSmK4Lg6BEZmz-OAyWmK&7}<7BWo/&Kon#KELh+Z[ZJ=[b)(UAf1F5(M!PyN=c0rM{#zJUcuZKdxAvfuQKnr&ntl]daMKR-}/8[3AoueQsVAEke!65pw!0$l.R4a]GMK!7Y?6RdF>PbN(C^k1EkF1Egn5NV7>8<2!(bMOD[>G)]&]u?bd4FG9ZLl@Q4LH^3K*2NMX1s{ZHuJ=lej56+EcX{$eNqIFTw?#PldF0=t%bc>&=TvNI]a#ARw)SZHx4l5nXry5juUZG{ap#:t7Pwzmm1:AN0lhIO030$iNgtB!VN.=$^?2F=S=2(F/fA>maJJw&T1dCQB%@=SoRw4z5A>nrrWAp#fXV[9Vol*C)s=M7rX?PyW1o2&p./:.}U1ApV(WypWr#Z{W?R!%]kCEh-vXe!rK{Q}X6lq%kK%fP(UY4e-cAK?wS(m}kUb5sQMinT4Syy5A){dM-OlgP1=E)lHZi3-XikxH-M6z<34d@Xt]hDlrb9ARkOvQ:*y@MgkPXC#Pqf^R5Ga(A[/]!7xTX>ttV6M3d9j5E])F)9a7).N.?6zC@Jr:#P&vKY$%WVUGrjjh$bQYRA%pfZaTnF@%86@m0(5D$fYJ/[c$OsyYH:TIn0OfqK!iDyY@aqWPUSu.8iMnBY7]xQ/*ChlJ>v6CtX0esChi79:05CpCKC:75W*#%}lgXXggSh{jDc=450w=G?Sye?HYy-p?)y8StL)RSYWy8E@TZnM%$j&1}iQu.[UmjP90ohtv6mQod@)8G49u^@B>*dpZyhY3D&6Ww>j:josB6h:]a6%fFyK-FU{1Tz+b=n*caOtXcc%yM]S3w8EIC>Ip@6R-Sud6J{UsS*3gD+3l}-Rt.u^Pd:^u:KL}R.j!U@:M{s2Gv^ZVH9s$U@a<=/9yXkRK=#^+sTS>RE/8CTq2U[/e%4S@?P)dR1qO]OHfAwr6T05C:H16jq-7=Fd!9N-y>6Aqe6e7+&.R8c2A%}E{GV)*^uXnE128@iZF7P.6{hSUGswZCsU8jM4IhdsrXP3rKsHQ9wlZo?R>x4tg&6Iy.KPBS^1KF*z9g89G=1N.?^I6hZ(&TKAl)vo{!m&u$1Gz+Svbikl3bbqNCo#v0iCAkS6wy}E^R$)yU{}yGTH2i(7-b$?&r/O@^ny#W9sNmMavl<@8@PxtdAf]9?{wxZF4e(cv[cS!ave].qD:d5xJ5D&9]/^!x*=#}fU-bia+=-2huo[MrH0K259U/=?2ZcTwM}Oc>mOc-wn.1Dh@5&{mgVOt}=+a+2o)=bMq?fzUG%^M/8.LtjbL.vW15JkKJ>H{Q&q7U*7atojz=Ho/fS6DIgeu/oixF2i>s8WQ8*mWD(AW{Zg^V0[I^0&Fps4l{ac!]R2xCOXyWG4NK#0Q1-Ex7[PhRukgQ3jXX.vyn2o1Vap$q?FV#8{b(vj()IMte=^%HG?4C(gg@QLL9Gyv9&kc-z=W5gxac0A?-pjCChtpCv0{XjQ-M%S5P@:G+W)RilQm>yW{IrAnF3h7z87dYJ<9r+gR8{A^02l)+r8U*YZzVTnIf06[mI7&97^eB43S2{W+>Ejsf09Xh>T&b%P!G1AJx5QWUtBE-kqzmsJXOTEf88302)esfS>BP)!mvF{x1<+N*Z:Jv%fN@1qdnLp+A3BSNLVxRakz+GIzVQh3-Ed%MHct[Q)kFktab1zg{f(]Y0$WSF0jVa2%n7):OIW]t5f8nK}j&VM<8f585Yd>eP+tpeB7Tfy0DV:UQ#lZw?5Wg%.[8cC!]%.Itlu}S2*kvoL]Oo0YR@[4jF%GOpBX#.3v+$Oq(T=31A^6k+}OY=1X!F&=838%/X{K4/W?T?%5}t=TbO]$j=>ZG3kDG2c(LO^qe})foBprzO#tdpj^*x@6K3SZr]RU4ycG&+Q)O%pn$Z(6>c/[*WM@6fPRezjEQUyb)$P6w6Lyl9tjTPLwQEVwXP9W8BXc$$:I}f3]Enr*AEgEr3vEw@N^IyLiDE<{l{&g&30h>{fqmyOn!Xs?dl#]nXX.*Ll0$RVaZ<3Ip[7%5NN(:SnuG1:Fe5B(KqCZ(c5)Aq!wk}5}M0n/DW[{<}4ga1OgBn17BF!OfPd(Rs^DMRG4E0mv?-ZZAtD5(vAQcMPT&.lM6vT[(v6j>F1cC{BX0O-eb[O@^Mjde<3g6$9t?KI7Rn>rk1#*fH]dMe0Sxs6gNwN[WI{A64)Bl>w6qr-zaAhFxa#xXeVtfKa5WtQDxQs[.4UaK$-y1(Kv0LIICO#)C*LWcASwEtJQ%?r.zumo!:M@W]k{^h?8OcCQaWHs6r?g!DK3s.&!f^45x73NZePV<>qlT]>0ti](qHo6QUUaq!Se6##arXz>aOM$#huhg8KwfX3zZ=>sIMJ8B538EkrYPZ}z$qE<E=M4wPRD54M@&>Os>-0QIau@AMaj%4kf<:?!+C)jCm:%{e58N>Aba!fgR$FmZF38De:%:wH1=tD!yA^/:Vf0/M*XNA1$qfc[G]dT31ROLR@EwX/uCO{7766iI2TZycTc!Dqvufz34?FCJq6%SmHK^*9WP5Teke>FfUKJB0Yp3r)Fd^bbe6AP+yCENu}0^jQw<6RX[{9!5KezqG$o1S?I

Bx(nKj/A$6-2*xijiQGzkNawpMHef9v3+L>h):V8]{l52NFp5>crCEq*#9[6@cCNzB:8/d+}dNL8ctLOjubS/-x>P8XM0AyAETBi+9wLTXZ?zjWA>h1/[>s^{+5IN2@n?ul?[S+$))qcY]A(-yspto?.Q3[ZyZ05D{z4{./u>bVVlO.7U$vXs$uQGa&tevZg!(ZLwrxrRFwqCG}22fN?M9dd.NllE}Wq+1&=7+!BFGe*21!It>gL-a>!wZG@OS]$^dku)W2FFOU9TtDrf7KBDu<7fi[Y3BTl].Iy:@S0MZ5QjaulAW4wb@et{0A.I:0dpyu6$FOL)nSIYuJS?N8Z4>&iBhiWv#L3d7@d9sEDkOWmv]l7s.f.Bc]}(qBD$wOiUu{C8-S)+B7e/=Y<5{gnWmzK0Yg!DKts&]x>g@&/u)g*t5vZIDfy7]sdFg66rI9s!LZ&P=!.v!:LA4^kY.crqQecu=^t^Sxb!X&S6?w[aP{DD.ZsJ@TAY(D}w/J<}bhl}2Lj.P>g+BNEXM4Y0U.f]3!-{jYEiLB9DRA%nz.?3OCT5[!n.-v(!wCdpfD:47MP3<&z$eaci^%BksXkh7^&ZPJQbM])%FF68)&w^3zn%y4}}Z*0s1QgW-/0GOvuuCeW./1aQlGLr/0ELYNcfjlhZxz1RDBN3#t4wW9@rT4av^>d*)U].>#]PW2Y+0T[w3]+y[+UT#Br2{@n0eIASY5nt8h537##c!6API6b.XOCIl)IQ(VXbYa8+!Ga{{lWqidHxvp6JbhulgMa[U.u8suChobyA3z@<5i6n{hL/VPv/.xxP%(o=ydb[I*Z+&879N%:QIIXzQALZA3W%#dr$$./LDj&/sHt)t:n?zrNhPTFPmI7#0hp4ge]5V4CZa9Lnovq>]OXRF8+xo!yuaUm]+xr+a]Zh-:]}roVt.[Ee?.HBvJu1AI(szZ]i)GVPIsrac7!}1T*X5n:5q(-1FhUvm]5Rh!1OXrcXF5Pm1.WI-*GgLIHbNszAD4RNWavKs:0l7uTHg2=9NRdjL4Nzcb4d%n=g?8V(V&))sD8Dx)Jl)>V^ui[@J+*JmV+LkTz]DE7M]Q5-Nolt2!sJV.:7(8exd)>RgpwZ*sr%mK+QnIHX6Vrz}(X1Dg1-23?vXq<}fl-LM7NeHI+&9utjzAUyAmZs*ZelWzNoTz-@OhL2eO=aqqK^Tp{Os>oyPM=g(u)Sp:#pF?2smPaI+xYxo-2>19PE>3(+4)p{Lsm5^?JWU^p*)$16oMVuty%j!}7J:s?mB3.tPzTE.n1hf$?fD]M[e3tWdWZYl?T4g^%8?8lf[X45q<4b]BZ#X2F2sTlB#dGz00>5ZU[:W7fJKd@]Tq81gSci+R(RJcs-JG=h[GoW}Ju4-D#g}e!/.NersnJRV5(vcXa:D%Ed6Kr?gaJ&=b]g*jF48nfuR>tybU^QfAM&v%w2=+[ovV#[d4>s839>ynE4x?2D5&%8lRX[(l:!AxWnGtSBcE:8fbUK](Bt.)U3:sU*H![G!A%KIbWE.fD?]H759wS%It)}ky@SW6O+{u(ES)/WvBXYcOo$T1M}adHDKstPG(ayLKh)BtB{B%()]7oW.5hWbQ^kkuS3m-m^$8o:1jT*ZlNc*#quxBW]/%}3ImOViTN<&RO:y+)L8cgzWJ*d+RbcOGUK.qHN7(5uJ5WfWeKz{!?v.Rsk*zb3B/[3b)EX&-R@GywfYuPmag:GE(o)84MTcFm.H94hbmUEug:HjxktmE0nal&g4cdfym-(*@!Pmnd0w>j+(LU}kWo+=O^!Uu$3!./sLANh}jK)#ytk:({KGXJ}AqJ0FDITZ/qdOHM&%/d-sbYKE{$Rcuv5u]%focB:0QIVfA?PJ<83b@dZjSe%:2QxG({lzz$xaBPUFn]Y@X26Flj^zMu1/hvU]KWaYmA&Y%e.rG+Qg3%-/1MqB/royFKbnny*^2m5E}.NM]mX7C?(hdgvkMrKBq6X>#Fmw-TnTL}aNKP!eOnBu$4bB6gZhn4-oQnXI+FXzh/c@Xx/m*HV@ND5/pVa5FBz2Y3pIWZGU3&iUuk!7Y>N5I?*l0zV2F/T$@R/Fd(!pwdSvm[UuoG7CtZ6O^OO!cezKGa2f)bnA4^k[G1HfqAp$EzEaO2TofEIjme0>b>Y]HzlE&9X*h(ywJV%/6BSkGl8fT?zVtb{P#OZ0Tk91fzsAJip=59:M?of#]iTjw-!G-F&hNGU^5BLSN//40w!X-#DqBDy*I)XP]YRAXa-dzmq5T%wprHl(J5j8q9X-&A*dja#Z@j1Q-!-W&#g5w6/LGEHG6+8fy]dFKXGQJ2?=6sNK7h{{3kI4/9vNu^W3+c]^rTl>gcP(w44Qxc!uuZH+hRfuzOUoN0N4w([$!HEy}}H7@!Hx:QaJ]:^q!2gO!zbY1-1.Te(^#3]bWi3.Vb5eV0/k/^{r>rg0bc4gt($7VUZf!7XMS3VJL#M8fdy6Xn:IqWYQA[x3t2(/$ov<}whSZjnEGq&!!wmRxv6Bw@qiRJ2R8mCXxXR=Pqma=bG!IKIxGBD<8/^a?=9uxG8zB990$:rmruw]6[7*:h:h&U:S.br5=5&#]Z8-o>fO4S{MxddM.Rk=e2oL}OUYayJfUrc({PlD}*S-&O:w[(Ct17sdU.7+q=(Z-8S4g$:Q$YA7FqoS%(Q6/)thlfRD{gJuj0br9t=OAhGpt-cCzLEh%bo&ZDO0jpoGTr*e*(@.-I^0umq&wPR0NX38@BW*gp6>Y8LkzSgBYs0:WJ@Wix*.$M&I!DK]LFb+ijt]EJ9:&.Rt5A1UBR*2@PorT%e}#O/ww^?}uPS{UD^i-AZ(1Z0J06OEWeNtRBiuUoJPHduHfcoOqU(K*@F[J%obrP1&@wHl{mNL13JU$oyTNg56HML]tp/aP&aG+Q]ce[0@R#D&(^z{2A#-[AQ9sE4ATkt^=jj]WeLD:y?*EC7*x*Ulx*5>*#SQ!bfO)If1XcX]55FkpR?hG&4J1rI7!ypgCCMFu>YUu?xI^k9<[Y+?DSXr6XyN7IzCKsi/E^OIYXC2Xez2njv!GXA0.-N0dE@tW1*h-oqh/XlvTZ6s{g#KSa]rv8&uk)t2p{Rx5)IR8kOElJZ%{s/PBtR+e:>0f6TpNUmI*zJsAwJ305%Xx%%A4Zboa}Y#g&2P4?.Q(I4hjzT^50hTLV{^6+y&@YW8Y}>vDL7n!A(VA{rgA9W4DTm9FrcQM.}E-OTKV9kG5>x$#?n?n&4qKhv}X/]s1]nd?r[HA1Vf5Z0*xd>=gb5l96B9[hLs<3d+}IY>Nk:>@m^1h9t:ZYYHRArJQ9AvthmWRD$4KNE)?RY3$vWb7Qz0(G+MQ*HggK7hJ$l/Ct4[=6m8Usuh4Wqsz]c/[h??cINLMIQD}%oIZe57Q4[G6DPKng0ae#b}&YLCe7HMUXeZO0gl2(?DpiCEqmpVBq!fiWCSq3XsEK-IM:>zy4^@z@ZgJK6V5=L217Kp:F&&Qhk@uK3O2lQj?jgsB*g#8>v@0)tBqgHq?19zJHZO)]&N(EFTj3R>teEgV:n1S6#PxTnZXnZ{oI)3l>@5C3Y{VM0q+0ezPUt-9Of/y>HK-GSBq%-g.}Dz5^fs!h)r5dR^x]O3Dv>o??8(>S(t-=Aj[jvXuXrfUp&BS=A=2fTAW?QhAnaC/er?[8:qr&V2ux{R%WI-(tHbxsVTdVrB$w*(w1(6rEG/.Rf@C?X%qd878Z&z)2=#@7&Ub=?Btk+COv?5fhxzrh[TxI[n6S>dR$z%8T+3zPxEP:131Z!t1NvMa?rA/B-pWTKr5=7wO@>bQGxp9AS^QP0}=7eJ.GGtg!T9KY-JyK?i8ruAO0Jmwp1tF8Lz-[qIZM/MOe%K{v}jbrb5znCEi5N(CJtEMR5J@G269D$hO}f29>#PWlxqcEumry[T3uRhm8l%-5:dfuz>uHy5}-@$EoJ@9lxMwjunF}:WK0foNSg0Uw<911&ON6=:NU8ud5PzEd{FbW7L*F+l%2:6?6{AGoD$+1P]SYFeBc]++&1hXGU+50yz$4kqUWgIQf?1peKDLcZCLi&2dzfEiB]V:v.j$t^9QIjGKByNNq9mU0vDX(LP&({-$(s#@-OlL(U-}y7bdmQvyigYJ&]]mi+*A[qTKZc)cEU=5U}jbdv2CF^mq9Glc<[U<>iKO$Df4O%ifp!3K?Ob(Fl6[fzzmvNpV0ip5)Y?1OhJ-+hU(EB!v?lmVoD(dQ6B5)7(&}AGGTEM}+lc9Ju{DOYAh}igjB%$eX:{.y}x/@9!k]IfYtjmE3k4wZVDz8SCiBKu-/U#W+i}l(*D6b$Eu^YP7&%l%I!}m9b4?aEiFfM9^)m?EdTV[1<:EaF=63j(7]98{?DQpKFJ0tsTS9z?Bd>7mXGMHi]f>}LxqIr.qp3:39L:skt6ADN>i!@bHm%gW:81KvsupX7/rcm*(FH%K*39HPjl61BWwWAQN1PHsNcZpJ+/$W)YnpM])%Kv)+>tOXIh?6a!WjPqn#6/)q)4h[nTFgqa{.FBr[=j4mgoV)-KE#Li[po6GbUp{{zKw.u)]}IroBHUh0-R$6*7s9AewB@2FgK+BdG=hU{UmpH>U-&[pp9%qZ$IJUP?ECt$TvR[)d(JHOjtgp+ZZq-E3v%!q%L^@*-1tD)kDbR>Lp$6xw9z(mrXcrMv6KSvZMIve1qN&.xuUW)[R53C2o:K>opfwz&n@*f={otlFbgPSLM!?VGZNwFhjeHLqz4dM/2+j/m.pbB0%c}1*H=+RlFKb@Z6iVyXiFpS.ZR6HdU&34u)z%474O(rpkyW>hUvhM?>hG!(#dhWya&B+L?u=XYoD=tl-O[jC(!I3Zh5f>Ei1*jc^ux:aU[na6LzV-eNK1+mV&$oAnZxr=Om$oEsM#myJY^V5(2>zRc7@pf9*iA/NMUSn5t<+2)5Bd?%vd&d>6A+[a2gSa::wG0?QTHGCwRG104T7K}f0UImsDp8n6NC.WfCc4*k0RC.^qwoVe8RnVd:Cyb@IA7K*37z:kjVf<*fwWZ9E=o@gWnHQpfrM>hDqlf-?9dcWXp.0Du:nG5g@IWKjvx[eRSJ0nBKEwH.h3I!k)s+6&ot}Xg)0N70d7#R]xyav4Kvl4^Y}!36i)7@r1?}cV?mmaOMzw?g:sup]H$JaP:xV$SIy=kR)<+1!qc0g.uvTn=nC>GrhPI3^5LV^j2%R6ELon+Y&ITlR@tnpT3?*QFV[SlfLfK$k@4e1GplADs1!zBkx&@L.8c}8gEew<^R}u6U^!Y9H(vi&]VLjWATR)Q*0/$tL}e1b#Jujadmx1&(-tX[Fik[UP:U8XhBR{e#ACS{ClP{*J!yaipSf?l}lm%RqnkfH5e6k7rtWm-p4AvMZ+L0iyp3/p?cAW{ZJKjzgyK@..lpzCG-RNPof0YD.w$L=]c.F+QgoS8M/$qk]yQAdR-lyS4JH!c/pVbtRXE&/JAg#ujZEuZ*zLiR!:p#PjdmCCtuFhn/rPlGGcousM3eP%A-H9)mp9..kZ8?@@fQ0flaQWrUqb=+i3!23rKw-9CaRe^sx$]-f34:V44[cS@BtgostGO2?U#8e9e{Ig387xE-#t^WuR2YX[)ddxrQnu$9eX87I*-kHe52he$+6p98xbNxICp.yZEk0CG#QroeSV]?YWlf*/U8OEkRHTZ@mn^XSClsYkk/6=PhL4ybqfFb)2Ur=H(P?zW/K[XQGL3d{@hxS}5tbj7p#gfq(#p3:!3si1+{-6L}3cxlmok3z^r5mw=}nxP1^gESvtazK{CYvy#hB:WoX3GtwjgQH{UdGFk/0gwk*5![w86n-V?y0]#d=SG+=RCY%keKz$]-#=0Rp-5)zIs)uBRZnJsLZU@8VIA[K]zwjzP%fs5RwQg@Ti!hLrc8Gdivzdh+L@nrv7v:E+&1Net9&tY7-aISkhYyQ-#f@f6O>7*L8-0f2NOFD$hNy3xoV?8-3La*7b4<*r&NpgF<&g1.@cLuRV)=}UqULwT0D5o(c1e>![o{OY(r?#7+c0OOf!(D3b>8GRg6SRJrwq97IZhVMGg#v9uG:M9*yGtY+rPKx{mJz!&pOC]CrXWTQGQ&4]fLmzQy(=Q>y$aDlvqd?jeMG/Q9EUPAtXd1z/*fNm*M(D=(Nn8Fr=jbt}@eqMitwV7[qJMa0P*y.PT.C:*MKZY96gSKJ+t^*h$uXyGi>klH?QXpByphht{9fV[{3W>=JNgpsw3j2dKycKfU7k@hVm=vO!Lfwh&k<2LKd/b$:}]%)vU93T[F4On[)6YM9GgJ9@b3Eo=o4ZNAa6NNukjj^s/4c@UOr=PfwaU7f/cr{*C.6:(=dno6k%.p8Kv:C4PI$?@KqmcW+KNf!hAPf^:S[QKXNYal08V&A)2(#G8C*DwiT#o(C2U0JftqJ=H*ms!N7y<3hfkCO#!*uJgB#yMOA8n+dw#L5Noiw=3%Ldz.+lr+C87&q0/#bi/:e[%N0F:tQ2OPZ6I15E)VZ*f)1*O#FMzRJMC^zB^5QKvt[]PBDcV:?b/YJ)mYy>Kd7ndZ-M:8k)8&zvJ:THLJzuqP]#4f4RjJ.mlnkhC2<6Ec>A2g3(ighjaI(H7s95f)TyIt::VAt%v-jAhR@L}wDQCr17!N8spclJ9vYy3bS#$pN6@x!J25sLd=9+T2k4h=&Tz3MX=jMM!)$?1*[+tucxmWT}E{59$Ut>!mGDaY]()4]>=j0ljQ]MH3Lfci6C$zo5(v$FMc6.IK0NJtD^Z.l-IPO]:^5iSVoBCDhW:^PFE.VkBC=68P?*0?^n?hd+hG21zNbS/Vo^+NfF:8!V&Q9P:o1gSI=snCprzCO+OS#A^Qbjw8Nm}GCSWxjn0or}u9YoG)L+[}V+0e/i!lgi3VXrQN8&g(QQis0H<+=3K]Ur(mMniBH>Vjl-}CzBMSUgQ[.rlvv?gwh:vkddxl{[3nUj9+SR*802%)mA6!mt>pF=b1rV{.Qi4@GV[PPuev{U:WT/0-3*UT1xHBUO?uigLjFPYy5+xcB(]:r{X0qDFyUlzTKksB-WzT5u8I#Nfx8Egp!AMob/augPTqu-AE*yo>=*Zn$^D92^#Y=DoQfc!T+Eg5#Ndx:0UvA7vEf0-[WHK+>+2JknY^{0MTOb<*5iz:orLcb%ARBmkTaNH}DCIuAmaOG]F*a0fh6-(CM}5:B)DH&0SiI5VMP/zVn*OW$NJ#d!z)]!CijDhKW]Cl8jg@=v]{q3xuO15d8qs+HC(&SYXG!smUGyuO:9C4x9]>c6Uxj/xR6?Lm=r[lAMCjSs%kaPW[yL0>fyP5>pPm:KcVuV$J)8ZmOxOY[jAI{tQ7M)Gr)G}.ZcD:.s<1z%eY0zyuLH}wZ$rw})(9aTFLC{X.vP1l9zEEvUf4k4vZgl#Y)G0^INt3CBq=5=@P{9!*+}uSx=:S+wmvW[vTV=<^qn1(wRb+vK42i<9/F6ssgyG(0i?srWEUjITX*8HA]Mla2qtPNoXk6{&HlPA}T%DvghacSOA]rC]CIm)B>gn&o:bK4:djJ1m1(Mn.$P@9A)yKqh[ewZ?2$-TW!H!Qr)hI=SD0#G=vB+bH}ma]@6LGoLWi]3}pT:mO7?v{EqE8J3@SmHsq0LwMZwMAL]U7yQ{Ww-ruVJxAsIAwhdaVosp:1gbGe]cWC#+fSr=!w$*TXzdVnW4XnzRxU0mGxe4JE>(xtZX4Co=71MRi?]tjjxatSXdWCTtfVSm6a%g}zs.InL19E8kQTlJm@>tk21]v>03nt+fnvy:isz+?^^#.8x3*crjQ02tf(+Eh[25*zgX0/UT-$1:))U:XTX52]^Ig>LJh4fVw!I{qknN[g?@%m.5xJ@yicFJuf.8WX5?vEAT:$r5K>U@/gbEyd&V3ROH![+XYv^N&r0!u@<&+!qve/jNh-bW>21^yyroHH9+}9oe+oLntjL=*MrJ&S+zYS}tUy%rvyw.w}Lg/Vq$CrZ*CaD+-b#tZt5q/#T3BJdZSB9/C*%]Em5(W+3A8mSliclwv97}vbnZW)/p/bgR&):iwHBVOG0su:)C?M=sDoDkwuUT8z(I/.dLv{4oQIVW=KV#uX>@V}/SfppNCy$+[557FGiv?y9$<[aqpFaiL)z?@JyIA*c][GGl2XDW*8N=<9fiFr>DbjZw)lXj^tgfYdc$s&]B*9p]/dnCSpCxLi^@wU^=7dKD?Y9)F$9]3Y?E.9Zs6Y^3^99RSVn>z)ujdl>#6T9EP#WTRoQbdE+$p+T]sU{([kO)>-rwHK)688PEQPf*tLaY9-0Fd=]]2-W^t5u!-h95Y^:}tkRv^kONIc}qW:^)]pytC?{+>R=7884Hz9cx6gq[lAo5GWt3Py.*bX4/l-%lu9ZA=sOfK}d*JMW9)hbw^y@xc9X)4j#OTeQIbk.xOF}A$/.G*]65n/K<[qlQ()EC&fg*%PrBV>B.+KrZ]/4Dx3LLUhIXpi8nr4-9?3Kg1fe!rR?Bzm($s{Wv[#g}cXZrZ#J(iK$%uXRtZJ8J?4OWQbD:*[:!OJ&jx>j)jXN:{VUbg-J:W3ldDPdw7Up3.mlQci{v0}y%>GQ6q<-kpbob?l+AnT!pV}620n@>NEf+ekhoKs?g?nR/iOrxo!u1mrAIsB$)KcCE.KG:4%RW+&:sS9?W:w2nnfpA.&H]XPRb5Q0-8CmQ)zWE!Gjf}dzfUNi5[%E-sLyIpSxXd&3AHqDhHYUK0-dX}}BT9yd/2SU7NMU#W64a$e=rC^ewsIc1baXV{GxQPar?&pILnEyyAgQrtSGR8cJ5huS9@:CKfX:vxsIAA0-4qbw#M:[M}T0VO}o-!=+o*p-N5+EIH2.3.Y1w}Wxqbu%:q@*lltUBhyWFaBa4x-iJ#v(JgLcsUZ!F@Gm.)8Nf3fG]pzE7k0f)6hBDb=ZY!b>wy(aN7Yk)vf9XJs-w1:Gk0FArY*IKG0><:ARU@?%NL[EWwtC!B49GE@N.iaKuaDGXq(Br%6qXa{nk@tW3gvSYd)RS]4h=7MOpO9^5263Zkz:v7b}}b@u:${z]M9j*=^l-!En[ba-6{NbC$tIFmk))Gb[Eyj{(YUn{(}MPHax59:-9HY&v[M=2H[Y349-rqtimrG3*[-Ik3Y/i0hk}aF?h#P-Fpz@1PS$*JwRk4#szGPn)cGK])8[Z3&:(x@^=wh^M>M--L@]-u)PBrobeXe@{0X<[7%QRVO2%S}-}Ca&.ZClo$wMVgc^7!U[B4l{h#0a(6c[wfuaaxL9RchFua?nl568qs7hRXau>XzQQMx?xQSf%dopODCS(I.IKA0BE.0RY)k=anxaC0V$izy8C*6)^tNWolR+*pA[dla(k#*]PgX=/DzM77ClkDTzn!Ru(>We65?vRDONKYB?x8-[HnCJ8oJ4dCzVdeY(aAbB]wx:XTnMUn5m&y}3V0R0#mgQ:-r$Q>/jUt]+EzV=WjuxgTnimKEON8TSy5&T?2!gy^qX>W^gW>ARqw:}?/5%X4W)PDzjV<(6+fE)-Ni458N*dT!](ZmS^$^*2=M<+hsp:mT-xkRcdrY}H!FZ/Wg&vxvE@E)xHJ.c./}(9bR/G9k4BoT(>?+RBT#ugom=[PPX![M2O}C?[dR4^mhVzzAw58/?t[AGb@oNbVC]D}xz$zdKiij0J)eZmi<=9VBV!CVAy9uAd&31+nxn([F8Q0C2W0Pk{5+NgqkM{Gh@NUCm{I2u+[OB[i7Ck5R^YKGyOMrOw:rC/:ryM(8+VyzDiTYo.Vt1Ev^[0mHLH5#h1B-jc6PwS7?}TYWx/%#eNtC%{HNH-K(!.IOC1f0SNUa:6qt&Tu@vnEX^[XfO=bFm/$@}0S}Q=R0s&S+)19[-p.W2-}WeeJ%Wz4XbEyaXSxTaofd>(i=M5$I[G8f3K>8d^)orrmHtgkJxN5*3A2?R{2KDK[9)BXv%5s1:a(O3%YDKDmaJMThm(h88L!0wSX(2Vpqmjkkj+B2IjHM6.nLn=2%x?xjRnjHebsYt4ZwZ:{7.dGp-@LJN!1VM9-G[+}>@ekO#Z4d(@ET6uvo!y$d[ANVTL@]l1-!Ix$Ffby#cectRiLekb(IK=5mOGmg/M7f]E2yCRa+iA#2^#s9WrnIPcLb*8)C3E(/S]WH2SHXBQm=ZmQ@[PSEZaTlHMMETJwHUm6g}AS4t&?i9piDy%y+)LklWRZcCg*[Qth?h))n(O.21=6#Sdu@4+)HPb>p-B=pYFK7t.$SI?Z@[OI7Ke-/rq.PC&Qvfx({[9!0MndKnMwV0cE9s=noxO^N0FLsH/^by?P0rT3.Jbxf}ZAB8z2y^d^Bb!P$Q5M}zl-mvaSqPt>dh^Svgl4yj@tR&w6WXsAE^-wj:WeX@+ZcdgVoo:BL/5$Dakmy:ByF)EF.Y5AUB]:4G-<@N-C]#(4J7#j0aHfCRWy=k5I6a2N8wmF2Ffu%3BBEE^MI)LE{eS&])yjax+yhEW${ix@Qm1TMy>hXKRcdv!9F>J<>Viwm)ZgzO3Z}/=]RR3fiWlOOD!.nl>yUua]&Ea<#n#(bBT.PyXyo6v%s.Q[Ix&uOB7kOGP1U+{rr/-uLetPvVbzzc/]G)?n^Vy{jiyzBp.Ke$}p4ISsnu1RI>&s^FF*F6!8(W?@TBiUjyS6.xi4Rh{zVNV5}CXTsAzWfI{nql9Q7S>rBkY!$4)$4w4?9*2zG-hN9@H2F]9k0=olBwL{Q?CSpuIcdS1i-HHD%z@M-O+?.e4xkf4oTVDo%+(xHL#wiR/7Et/oANMBWYHQP#kJkb(m54xOSb{{IKT5651{$xIwM{:GFPX1{EJeL6mkAH-=KCMPff}&a2qv/Q+W-l(NU4u-Ty2sEQPARq3n-n)>dR!/8uGsCVD4r1K7E&VniFVSm^Qyh.6oB1V7/!7zzbHiY&I7fLg)MCj!w]rOUPRHFkuQ%to]jj^(gtjMrZFzgtL7x*Om:RHVJFj-e:)#o1]fLLDp^73NBulH?jh)/2wNqaPXXVlYuI)V*r9$t*C{K6!hztK.2?2j1*fJbnpp?H>f}T5iHu871Tv]i^DlY4)A&VdBSsl-4H#H4TGvq}:^f>=98wT6>u^lxGwg0.>L<.87{)$%z=*^HZbt1m?GB)n-v#ZkuHxFu5(Z!L?>0jC!}@i.%[L/8E&z0T28)zztX3VAy}Sa#rRK)PQyx%ULF8uP1AD]p0iaknBhEvVofD7(bqA]NHT^e)x8Ve3rYC*%K&lMO[d(BUp4%!d&]Lax!Q8$o4iZAH99O+1ic@#nfA-SVI)BDXG2l.Rw5aIDtNhLQpWaDh{oLZRXKQT}Wf6#6L.rQf$l<(bBkgk!t=i59F.R]Moxu>L@3hf]?!55>zz@*r*DPZu]xzD/rfd#9%z]XgjPc@&L4^/jsVgODhw^&p{Um2xiT5z9f{Dr>gT):M>WU?wzEZae:pP)^k!GsF4PHvUu?XjqIAil>}D3cE$:sJTvpw!<69J3c=yfJ0mHq7o*f#ztuFkH8no6quy+S(70]/FdHys3GX.=[341V?1g5o[o&zdKm!G$kOhy2bZI*}H%yBowt96cQ^40FwnEZ/aevFV5e/[w2VBU7Nkw(#v!GV1O++j}J8ZI]izzg6>$E21d)J7e@+/7>Aoff%4YbCy:wubEAm}3hA(w)T%7!{&Z+zwvXh#Yp]0S0rObHd}Oa&k-0sZ&Aw7jK>+o*5qRmy3!#lc=9BNgWB6LGS=pv^r{nml>vCNw{IQK[5-duMy++$(FTUH{m:4-YwMy?#4rmxC}=:fGW6(=F@dX(uDZJ:l}-+VM4P)ejyZ8}[!jy[6zcC5Nqo5Lgj?RcGqy^Sh52Yb5+%F.?m=:gAKsV.i5l0-n^xUS(]s#Y*^eapG3NnS>#zGLpkNUG>h:jXr--J]12pI8ipzg[..)37(z5#*ahIh*(rxg0fGOWNJ5LrafOHTK{Nv-TSWnsfN=U]g03sZOcm9}47cZ#(0Ftp{U?}TSileoS+Lo^dA5eV@m)*7+#$B?0xca[bds$PMa7Q2wTz<-VlyC!okOelC]ZKP3wMRU2ed0ATE.fl^Y40{I(MyROb>/D15C3pZgav+QuO9)LC::-61tb1]EqkF8(TZElW8Vwt2N291tBxU=7.U&?#=rcI/KV3KdY}[7FOQa&>-g!6//0PtC>VbD<6IJY3$#R5elj=PaCcsxE4mKgxdTc1soc!Ztg1n22X/11uDh+Q+z]%jK%7BSKsYxzUiqjrmap?P:p3aU}@t4MOl])fhHTx-#Ji&>+9G1lQRAmH)uh^{?IP%rR>.Q@v@emt#itIZLR/QV@$#Mo*/x/75}:R6=oB{3uH!wcX*s.z[{{rEN*oR)+x?d&23oWcr4Z:}kDN{lmnMUe>Dq^ks!j8!$E=)X:RER5ro0T+5TtIzo848v!nqM!Ke{:#g5^PKLFH(vq?U0-x/ap.a@jJc1KFpeybGES/52d.a=zo015664CD[/P!%f@N7!]>9dmHG3G6%R%NJD4I[D6!3d8{tl0anq8.FF70KL6@W%Bn]#rpk]BTTw=RH!jSemYo5X.l}@mX=f.VdALZNTZ=&OdcI>m298y4{7jhvwfRQ*Wv}e%pToF2J5e%d!(U!ZsbSzK>c#.gAl+c4*Ma/M&>Xi:y2N*xWV-EZ/9q@hLVa8&syuXCCW12)k^O*^{37oc%X(Wu6@?etwGAxRVn.Ht8{TQAY/X61vp6+i*{vM{KD@f>mV!rR/]L*#uFI(Lv(<6BSJg/uQS.Anpqmh]z$6uDpt932J7%R!xmUZyv)r]W@Mu}h+s5&FdxvfeW}M0YG]kZWHoIBfXKk&HNn$16w(dJf&9Z/RCmh%]L8pH-I%m%9J2.-rf7rp9!S&vL(y6:IW6V%yzolcBrAaWKQ)9%Z@J>p=OYX{TQd&:}5!Hq784OZ)$]EG[)YAcIWP{#+zkla%OnI{/N?]3*N:6f@sIf{oR[Ls6qDUA^OMf[&Pvnr9o>xbRYF-96zikxQeepJ.:87HtI:8aB{8eUCPhgdyCjzkf=Ed!Wy3nVHvy?Wr+:Vf?GYj[lgoEJ7S]nEw5H?#zM:XOy<9^%/0KXjt>j7G3znabh8keljADLV1P+T7a6U]={u6fC7f$ewj/UHy5vwk8Nmg2$10@bQ73s!hbu7^zd&le[Q:0=lT(DUjXxaFmvGfI7jVAIdLq8Q%V%4b*9I%F4y+cLdmV6S8?29YNIJudzI-LA%}Usxvr[@PLT#it%lWt$-tRx{&h8+aJEvxaOlplg2I^iZbVXtC#=321/DUBnljwVKy&Q(y93I[Efyl?QLh8Z-EDA-6D9@uU-yc#mg*}1O^sg1[hqTthRGx}I.h01&*]sCpVTagc1k*3(MVCX76]/ZXVgB%cYdz9aimU{Vwig}Ug!+x.x/gsTlYFnqqGv7eI@5kEiq&-:Gamc^j3]OK=d[%jQln7kha9cB:)BPDOor{A]=du.<)^qfrhmo?BJnn7xIm>0wU37DbQr4tq.hQKLx=4K1/3xmv4lSEpNiF7GO7Xuv[pdME{Mib5%gf2Z30rxf(u=..lb35(U6NCLBTWmei5O=]!vNStEAiPKI-o>RO.vEk^[Y9]!u>CwWG>87dx}rLIfUS7$G9Rq$CptW51tq@92R0uL?7[-.Ux%CzX@X+.gAK$tG3nrg3!n4GiEaAxM>$}F}Ak*/7l3P?@338t6Qf9zZ5oY%=NAd!aMTa-u>p84GQjWPo=0g%FU-}{6eG-6]^xjW6g79@58OCi:fGIUGe^k>NK)/{YI@7<7%5)5P3&}Hw+i)HDIGMi:Z.:p/!i%gZBD^]EV{x:nb!>p88w!tx?q&Q+>c1zim1yQ.XpF!bC}ipw1Z^XPi]0mfJx*EsV>op*22xL[1G%ws]5a%8wn-SHu*y%D+.FodT]G5n$UR%pC4Z09W1Jfge9a1Ncpd-z!JYD=wZ9ZoO2g5*5b75bGWs1MKZ#wQR0S=qNEw%mU}U:ZB3@FyNn&w^+nqRm*s3V:yMXHy:tcEOWvX:XUTOX66WLd@Zg5@?SghU6du*Hys/w}[g2moJSk->sf7ve4lLI%IMxP/AHM#Gq58>u5Xr+-xGR085exH71?P5KHE&%pc0C985Ppzp4:F[Fabw>J1OjdB?%eqt&]-TE82y5MKm%mY(Jx2h<&Kw{u20+f6*f5%s^k{-y&e27x$evv7a.)zUEQ-QtKy&vyJ=w7sHVDjjC:TKnD1KQk[WdvmhUemuY#IRbo6XN7{i%2)#(ay#zDeQj$gZ>zcoAi*5KhA-R6A]X@:FEQ)P5*%30IiDHpO+136OOJm%WP/RX-w.2TpaUU?D!IF4xnstCy1x![T$w9<}mCx(}AL{7uh861Q7-c]64spBFIb-CwZK=pi4x@UFUi#??.eiVqhO.M1-&dphx6/{!jAo4UP+5%y&O{4.b:ZR[fI=!K.cyn&)J7?L9JDd-7Nv)A&i5PIm*wdZj8wb0s)yo.!D&oLy4Sdk)Q4}9MRLbe.F$*dL+e:pXlJV(:841D[f?6A%()J]>x7*TrRU@b:CD(XB2yFtpyYz9WEpd^LmE8mudlE0&iwv{lj2JpXu79&rsLodHN9DB4.0Fvb]NmK[3*jV4OptnQf(p/MqdCR0x@WrjFWmHR.>5!AChgq9Yauup.b87zU9+i5OxLd+l]9-pyl(@9V.w${oCS{S27TZ$NBl%<6%/w1ou%tLI4aS(*&WIDA00q9f7Z>}5VxK8IvSDyJn@}3XGNHqK>ygQ8Fn!7sB(chzcXJK{Uc=Z^XpEwWs@khY^N?.mmOsaBhH@Wuy[{9Gf@evM.#(fkpiDlO#[83WG!+8*}%Um=+6^WSGcP>Rz!+GM00O/?{A+4^sIQugv)<#i6F)W&NY8B*^[z>6t{YpDwfk{UH!-=cFpgK/<&WKYJC8PjLqOH+z@LNEcMx60yCJ[2G(#89^wuxe):^MS.G7=^dQ97ayjxs0XaI=$wcR:gRkfTbgE1Y$lOw^Xwg?p1iPSuXr&wPF/)(TnP85Po]-eVqFRYnKA*^S59Tv08NY0K0?-k6l?.8g?o=R2#fPd6:GdqxmQTD}(dH.u}YeyR8(?%3>btNrr9>3(Y5v:pZ)5THQe^&1[s)*ALnU]JD96emWXzKNeJ[q>9fO9UQ^n8:ei^^!$I1#>i}N@ZSj%2nE6b3svvk9$cWEq*PQ2kq]zUZ2pE0IKsVyBOG$H=v9FpmRKrov/3A^tMw[(U1?@!k=bBPAoN2mo^)$KufQ-f*fOyhB+f+YpzCt@qTCI%1vx%[l/%=2c{fq3smknYo?L)]^g>fiTJP=&/68bsQTTp7b]A48pB9Tio}A/8{ix+60(3dDROpT:-9n!PX>$BDRQ{ioevp2K&}ezy+W41-KYy$lj/]+2{9H6wZ-B0ex@1LWJg=/ciTgxiNk0s:/6G)[VtgA!}Wr1{-UiPqWoWcCa.i=I1+9.+bGrwI>D{MMm4^]IZ7$uZa=vH9JK9Sffp$X-%JwT&6C{/79dp^p?0D#Ib91j{z}Q[ALh/G/fj)GPQYR?gO2cn.JpkeGRIEah)aM}&VolC&p/jt2A51ZLeI0yE)poZAhkIB//[<#gIrfL!2eP]/5wzzZ76I!MytBpLgHfqBFgx+/ZXR!H%>yS30x]G2S2$6Tz9e/ng*^txKpOoS=AJvnX@W.!YPin>Wh#R+poJ!g$m8jf0mj)vUhL7^:wwr-UvZ$YRc@&Jb4vN/d>[K.9J@Tg8m#zMl>cf[K8PAVB]c*k.?P}L87SfX6$SzGcxnYXY+(%z6}hNjC1?B!7?kj%]$obV}&)MIWBGHWGOUWhyfmvy-9h5LYyf#6QuUaejPEKinu9h0x0Ci7(3(y:KD*0[1%k}}&v4o>LzSt}NjlcqzYQM7>OhVswPecDpQ?nf(*&5?A.T.>$yJTjQjBZ3zRUEnmy^C>k$(1B1aBWON#nP&#owC4qF8E7Vw/!FffBrTeZzAMBN&e5U?4mc2dZ!LHazm!bQ{QuJ(kZ%F>[dMEd2bc9oxS]QZ.>(L9d3EPAW:Og}&)qR3y:#XZ).>%45[OG>^BKcjz[$gH23A!ER2hUHAl#l*V>tN}%{hAv!AaAKdAfxZdJyQnU#wDy{LkELZa2L&WelWo]se7Fzt6RJb@zMc*x=w/@jKi{nT9d?zE#)He(aU]DB{.UljA8CC!vr?4gQgASBcwCAd3Q{bNjrucCnVAI#SwCl.]k=mg4WNXa@eR0(JV-o)IOt$$tYGYayGEc@y9D(KA<7sU+pxRPljUbp7x2ti@dVfyF9sHV&[BxVf{@:h:wq(82w+KH7o^D+C1lYu)>Jv*0J7lS%kzu3.&H&a7jyu!LTh+z]A(f[yZY/8?P>YQxR{N0#+ut&V*z!w>RbN1^)v[(2+)ZGDfzx]p{TUa^NA[R*1xfDc6Zy$Zl+6EKTFGk-2Nyrv9wJ&z^/*Sv${mCr}ArEzQs4dcrqqb0RlE7p=w#yRuzX0(Dr.3BvW3TM%-0(x^)(j>R5XS8uOGuvs:eQcJs@=K7z.zKL8u])h90rL)Cy]F%6R}1vLpq5ig4xqs)p3?IAD:uv=>nC2b.[U/VhN0wi4Bcbh5QCz2z=BEosiv+IMHL/og)Wh-oC!a]y{hF(V}/(CMmS0{{JT?mtOH4Yx/W9o}pnE^dn1Fy@m#f7XWic{{m*bY9d]l7%=T}lP^/)6qN@=JNQI#YRm#$FrLWu?5H]bm3?5b1MQnQ76l(g9<.S+I.gC>rBLT)hW8zBpsF@MY&zA)UN=yg93*WQ*ofgIc{ppu=SHWfpUjlDljW0fQB1Fds4zx=9kYk5pUCpdj!HZR[*SXfl(M/YJ[lJw:pD?xFK+>#S<[9981(>O9pcYE{[spkxLa!{@Bw+?S8fxDzoUAp{r=jHSue(pN}mZ9^*7qM{>Fmh)Sx{4bKIe?L}erc^SY7wD9W!a:5lARgxL?$lHM}iB$[^je?W4Ape6u.olvItg1{2=DzWvb^GU])JCz=Qi@>$qm[2]za-K=&DvdCfW(8r])U@:zP*osIzp]-R1{t*4-Xxt>/]$(ICh{)g+=m75DuWIT2yWFP{0GE4nU1da-js/1qW}9V$uHlpnm^I&%3TaaS)vF/5plxX#0HI&Te>S)n#Kgg=etEkTf>0Su7.Y3y$Krem$U4PticpEorUebo5R(Aw^WXBV2zi@K6hqM9#/NwA.?xhOq)ol=!Hd!lxjMm(pl.0.0#NBjj(1F^Acim/ZKXrO9+Vy{pwTOn1LKFq5ge]yGmdUR.8q.Z%TB5dP?z{=T*6?YId3(c2L3&0}3=h7}iNQq{BA5x$!az+VXE*?8}++U1U(F/6Hq{)E/O!I^3+p6[1]3J{D3F16}!)lD+jpKdhP(RtyXmANHL&{^(lhkOV]jWh&2t/5gw[w1iPki(sNnh[K?0?DD/F!*YleiK{Y#=:CZBQ[[/fE&q8HPMd[GqMbSf?iCMrc$kcxPWi=)Hl4l@k(1TSsc0NHNJ=7wkr}32q0A4&CZhj-J/Tn/2E:#C2U)LiHVY0UV[=A}]d9$}FX!Ur).Xkbt:wEsgMOM6Rj.yjk0&Q&LH^P=cYBXm)aC[?=9OP2l:DGMg#NB&RLe2cv7Zqd3T[Qv:OOQ47jB)}NF%<#&qR3t+a{SLYqux/cL4&qMM!/#!2*Bfy1HULslQPL%]irD2Bh%77l%uwV(r>[(ZS$U[vz@[617tl6Q6.K{xI0UEJw&TJ!os%C@j5@Wi$#3k(>MNkTo>AwSGg1[jS=^7+vXE@[+(}v9JkUXGk1]mg/(Ro]OW(pgUbqsvH(fLJ[PhWt93nh1X%]E0I%k89VvE-@H:dZE9!qhYI8]RCx--bUH{l)/r2dLha8t}17wiKULAf?KQJUU(%R2&6%mcrHr0.gAZA)sh*qDPui+O0I)sM=61(^4j*G0Mi97!U5cM34hj-nzhR3:p*MqMWE%E/f%%j)-v{.kc!H2%?j==hjMm-PG}atA9pCje2x>o]lCsGml/09EP8h&Rr1bDDaN%%7H#1zzNpX5pd}bcjE^r9JWrJuzD%XP/YF9Jz+O:XB/{^r+WgW[BRj?Pu>*eK4Eh6o?m8VU@Mt8M7OmZdS+wtBdq.C3FMExlU]YrBmLEvR[!!PD5zKFVbBubmQ85Z@0!n$t$PX4}Gy+B3ztK.(GjSHW5Fo>uV(s4@4=r}cE61c*}YI8nK*A9r&B!QixcypK%)[y.$r.YyQ5PC.3i=5Qi}tbyZcl5CK:hetlxQwX+wKYwWy*1j5?C{:)zdR9#FZ4e=^9?]VD?u1VpdvHn1N2XS?.dQFDlTH{ROGzZtuc:tZ?uBj=wjFgDdq8Bo7yx8Owszm)hFnut^hLoNy.l8N:W@}L00yQd@ixfA{kc.yaw/{Z<@0LW^wBgkP[S&5F1*K4y1Lc.IJaFfHEK?&PW(XgK}+{gPBs2su)}xo]r?htN1@^Z-{dp8}]P%m(Wy=/26sH2?@=2a{@%2Xz^eeVaEz!6QEf-+R)Lmu8s&l:>]11h-M[-4M]J!zc:y+O7#OCuq.&@[G(i**K$@GvI&2t6K!i[STEZGND7]gckQH%fDCV]LjjtnUKyQxlWWGx9p]Y4=>3KF45jxF[+fNZId}rLh:ido+FvBCfp%Mx:QDoSmDv:DHE60WD2.AG+CszhcvLU??QUWC4]r-#bx3SN.Lbapz3?H%T7Jp0mgCZqsBblXzkO?a):=U4xv6Hng36]s1jX5bK?w7HgxfxYEH.G8(W9W-(s=?za[JwGmX/*y99i^yzJea%N!)%uyID5]4p<1d*UGi7[Og^E-v?}Ks=<=^}76NZj2!wFL0IcZoNhtIM<$L1{y/bt?e]lT@hRUPjP/n}]AE3Akr%=%GNB>vr]!-ZA/6!o-s>23xmkO7UHdC:{3n37NIQv8Ds&vs^T3vpQyePOeW9.N9}[M-wx}-OM@]s5=Wa!?Zj0aWJUQ(y}Z6w4Ix44u^(e@oc3p8cQ]^BbD&2JX>6iGRf]D}%R5=bYr=nl=b9%+zqOonQHN@jLj=dd+e-?f:AC0.@&$Ct4)p0T@>PZ.vv-}qBIPzEzmvIE)Nq!Zk(o7ye1Z=+e{u}9FKtys!@qn[Iw%mVp-e33M@P5Ygw.X@J2(1-tq88hX18+oJ^8G<@BD>JMOp0hX>E0dx?xjX@@7%p8nOaw49?AITT3lNuowYzXCjU5H)U3Uiuxm*uW5w*83j54X}^q=0P:=n.uIdcWNS%I1O?]:x.5m?yFVhZuyNS[*sT->zD-dIzqq[]4*G.c<3tdICEyi!&kP]EZW@sCxuv7Lg8)d3kgK}if$z^Q7YbAxpZ?&$w?q#F]IzH7lwkxkwGip=KGY:H80O&L9xa4bQQTZ5w.i^kh=6urcK7kg9on0jFsLyV2O+%l(yj@tu8@eCQcLbB*Vj9ANQC39kn0R8N)(qx#b9sf^:wQO@O78h(Y*HvXIe2^Xl:d6D/:}H!ws:oM7dXyzadFVA.&InDHH^8FIZtBVDK.d&5:SbcG0o6y$tIrR)cn{Gty$6&5EVbH^7hz8q^lEeoqh:2dpP}I*vp-V#]Bpadj$VbrPR=v759ie@=]w}=utiRZEId^r}?(8hpD1lXXOdqM*G3IH-zZ2s6UU#1hnoPlInpt)hqLu2YkjZHSj0}WERXvnLNT$u]Hj9^e!R9ZJob(q]Z^.tLVD[/IpfI+RLTQ!zm*+N/#IjR(UiZzp[s.KhzPANg4$SsVX@Zwqj>j9IQZR&$SaIj0E9[rLE^/frOux5R8noU{KTCSJP/OO}a:028riJ)xn(dvlz!?hJ#D+$EcV{.hWxli%JJAK=?Oj9Q^^u>XDj@$eFB36ptgMpmTjGRg#L3Y6R)I$W@e]/gSEFXN!Lu^BRca7F3gQ#ST0VOKR.!{hz/nL{)pnycV*tPz):*W2hYxy8)QV?$C]hvqJ.8mGmESgZ@?y2OdxOi&}nor?t=H[4PpfzP6m+o{*k16I!9]^Eg}De-G*CJF?Elb[%CcDZv]SwV30-}/))tpnZzC*y+NUC6@e+l]S+*(4&s:zuFq.XmMFGtdnDo6*4x8YALkD3Z42*he-oAuuzr81F}IH?$#ElV)/{RX{NeXk&QP0&@]H/!Hn*]h#IR*H/kT#DoB<2XHfuO%p{Fst3xtluU&K2z+ETW=G7>0kBGxAG8Z[.gpqlv8G1!]Rr#TqiWE]PVXVPEE3$:!+BiPK<33+qu%-sc#=?}c+86dle!r&:IBG7By?Y?G[w^XebGCq1c@L[kHJnl&R^Ym7-L$lD>SB?GUV=2?g:QVogf1X{?KNma.$*vh}+0M0kT.+Kzyhg12oT&0/>:h?=T5^fY5$HA)^k)Zg]E1gK-^74F2qm{KuVp9J/=G%a8?<2BrjAP$MDXGbJEp%UwLL5]CF1k>@gt&X50wbfICMr.U^!kzXAAXgi04S04Z[+DXUil2mjq/D4W%{$Uy8io6XuT^9gZO(X[a25otBjx>.0Sv&Y2&3xj!YFI8z=qZcx*Eow]fka^:iP.Mcwa9G{e>7(1$lYgO?BBM8Wj/<2kcWU!JArLs+(zMVUwvpyTmFsGG/@d6WvJ^:n]&+]K8yALrHBd}0zn0hhK>uJtLr&Ee!0dHr@Smw-mIj[.HaZkh?]75li5NV$>}bg%B1NKW(Zq=q7p!yVvXu)-u5A4zJHGl<2eES&P}mK=ek*/v2U42v#y/4Hr]*8M(MQ0E?+8=pyWR]ps)+w?=H8.!HlnlSsX:S^EdvZ=UQV]eDO$WF3*%hWCcXQYIj>c}DlpDBMDi0]GcGKs[cE^gmC(mH&4FPjW9@brn8J4rg3%WH}G11FMm3I!Or8>[U0vmp5=.r%&iQfuT$$y4WITS^:S85oF%9^XBeuCp8oC=2d)S/.9SHECGVezmy?Ohx6gHc@=Xn:{oZEj/yY$#i*-dyyJeVw&Y-%jtWE>TzO6G8yWb4tV7)zI(r5frmQKpLErdy&*WMe$^nMq-SX/$k0?ImDEC1Q^NH{Wy-GOVbD!1o6uV.r%FKgC.5/#xP5[[*v?8AP.ljFFi(!m6cbdsEi*BC0e.?oqzeu^SvT}QfWH>N30+*gc78jTq$^l%OQqS(eCR9&o2/fn2JB[}343DXV0@pZK+-]sDN[eM6oRUmNgyuIyE5O-N(WPUxi4c@qxulPv9qVAWr@-IYD4y$k3([s<1G#-m]ie6>osC*haeK}#09.as-Ocu>^D2HIbpdOY==wm!9lMT0rw.dDbvXY@4zpN4T}x?)*f?Wy6Cl*u]OfJ(0o4igEq43&%bu/x-D7&kt!x5.6FSrrv+#[k9yVKn^Bb9463f@3xxPB]CE}MXGL}&^Uzby^OCq:JkiG}yq/(?tH}zIzNZgD@WGOn1]+B]@jvsz8g+1M{9I79+]^2:^9}4R<.#RZ)ss[7AvUEU]LSy06/l0l^Ld0mgV3XFey<}zwNz(5I>>9Q8l-??qj2}-1@?j}{hXR@MXhgb4I>J4IeMRjSY+hXZ8:/O0qBM4(TwX:E&Xe{qNKw2*sPmTIhg3%e@)**Oe:E0-(!x.jMg^2?-[A8ZoSU=!93^a:#Lg-z/0uG!O!D%l0aT!VHl?ay+GspjR4hhwIdVD1Un.7gZKc@DT^!2n$wwqZ[$Zx4>fjWBeqJM5Mz?N0CJ4ZoHGHxNnMhOVnjHAob>+*EqRdIqoK!)u%]VoP=WEhcXXQF!pQ%ROhNob3WaJ4&ue+&@Ahn2>&ggNLmG2p7mF4hLETT5X?Llw}sb@cF/1@lWFaR/u+iz[D}u1+6yij@r?nwGdkzi.PF(p/Q(e*k.+x>z9$jAUtf%{ag}0i8J)&=oo).uXh%c5ZiF:T>$C75XuYumL{n@]^A!A-OA0mPjT*tO)JF]T%l^{P}4CAAklu1#?2RTi8iFl<6WxepVSN@}1rv&{iZoRsn!p[MZF3!OxcLoi8eMlqY%o)++JkUb9nfQgAe:bqsrJNVt^E*Zsdhu1eZCb]?0<$.7cWQQYjs0&u{81g@&t8={?<.j}?XHr^DUnc8Mw$nPEb9K!4[FPmqEtEgJQejK1cOEe%Qc+j5R!xBN)B:3!Jj9lZ7aVGDA$X?vUqM6SdsmuARoe/1VsuFKAVs(07%ruJpic4D/V9E6y4EJhrSP-2moen3U0n!yS#JBen?DWV7fR[xamt(E+0%F{ifh!BZ%BTChZnSXujbqZcs)?*/.%tQWhnW@Ymz-G*M+O*WowO?{X0Zei^:ydYl]X5RY*Wtfu@v#w>lEA{Ta)yCWfq@j4QJmgePw)-GLeWSKz0$b*/ATY6ci}Yew+g$L6iv#6Po?pkyo?H.4#0bVJN>up!D?#DD-1@>I)5oxRKVmKRs8h9IU&Obc/JO}&CAgb}8<.<&RT5Pkfxr#D/@uzEE$1Vlw6q[:k)k{fCd+UKCTnA4Fku^ZSDo.Tml[MT+@?y]STa939VH:&xP[[Cv4wec)Q.vb]=x!O}226HNtp(/MIevJiH(f[i-j>y!}xxOY3110HXQ(Iegkc5oCbvl=XNF!ONIZu$VHV1=0/OH(K8pk8B7s>N}E:jz&FB!jkUuN@:$:*vN=x*#l{[Fts2OzpSOFcsc}+#x%t^f!*#+=jHrl*fl%?r{-ON+R&o1HR3Zgw}7(9Av$&6eQeZR8NL8+tYs34ru:mV+5yBd.)F{HS4R>Mr2]xg[=FZ/8AO6R^eODMA[7{K&e@fTP:o@hy4[^<8*9Ue!^i]2}yXO)3cNcoavw7g/dtXaj1E>4^jz#o]+nkAZExWwY&@t{jSe$H{/lTb8%1^MgH/pP.%aI5O82WZ0(Oo5ee$6ws!Y8}Pca8z#BSQ>D#0zDG?jIG}!(n7I5!NMBk?)A$bGR%2)n+SX{c%TMEr>DP#z-.Cu:)1#=gge#qSJqG72#hjVwQ.T2y9/OIvM}h>&{WgGv(WQVkzfG0Qa:$QyJ{<0-{![hWizW%l(=C:v3F2E*hlQ4o3MGrcWJApxnrxg=]oC>-(zVVJuTh.h6vae]:v=.lqJj0/9gCWjw4CMLbY9w#MpaMi)1R}Brt20TYGMbzb3lpkjY}TpFLB%}:t!Of/?ONiNZxS.ZdfV*Sa+PNf34CfmeRsxRsrNDPN=^aO2/]Rh6QcCVIa)V7r2m&Q>VURqr0@:X.#+Mo*JT+U}{zcGVx]X=eKi[mR)Za/NSR/m]-]$>QrJ>y:cgv??Bos19te>jTWj:Eld0]<@Z.Sj^l6Sry8IF[.&%4kIBx-%i*P4v3usXSAAjOG=)wfE)U!8iKl=fnfg!>ZNwAmbS/]V)s.w-n2IWLfYSOXNXYnR%^o8CAR7S&^-QjcOvYUq#tTj)/7>:{S4f}gocJUP(X?nFqK9q*d6&h-EevA=J2:{vkOk?9MVj.(K1CD766^*e6/ZNdLz1L5^X-%QF&O0$:OsrwBc)gHc@dH{[VFH(^pkbZfLz&0GBiAaY!(d6Bj6nG]G4!+YOwR}=8S})Cx)Pk(sxe#{T(EA6*Wdui)ZNtzRSWPBek>9zY1N)^ieUet}rb$MyTGd{9^c+??l7oppG(Y4l+@la[se>{}!4v65S4L}vqwsxz.YcFN.@<6Rb(I[$ooAM6o{6Ml]ZmkAL}yycFiNWkY:{:OkwRCN@4H{6d.y(L3QdunNQc0L6$Lp3nW3]x>)1=fgSZmh?W@pmos7Jq=@IxucWnwe>w1{7cNif]lsx15n)H-3^>ViI%4N#YZv3I)7W&vdlfT1>Z$d<*8nNm9SM[uia:I-^18**yDMq1#pW.tEzzQKVE<3XoyC{%=yl3QA+=b1jdV}u:Tg[T>pXc:/%bm[yY#U%l}DYFq*?ZK70c%H6-%Al/1%tZt*O>^$3+Hw9np/)%A@G#ViG-ViA.h^IQXaEGNQ[p%6]XFc&zdX)t9NK.eIxk/r7QhgOpDq(l:#Z[)mY)r[kYftgg4a2}sZPa9mIB.E9e[UfIrbFM3]$^KoqIrh[[4eKX-@c0TB6FY:an5S0x:G{8mNOx0OEfa96)d4(Ujkli-+h}uf!}f}{@99{pI2ae)*4.Q}Rvz#:}?(vUpAZ>a:8UQBoxoB5{3Twr?6nBNw8*k/e7ycP?>9NTE?lC>S:K]9HMLIKp#sq46Poi3SX8K>u{E42.iRc%ASZ7q@2abYEf7nGPK[Y4viG%U&FL!wO!/XeKAa}C$=kw}C5-$]HcY//:&?hUZ?W*Z=E/)cMf1YKNI85ow!2.}arLrMGc.d(bmsdH8J+EN}DG:3ZJ&iRGF.zl40[KNIo{HkEW?s8a}?ajy.7j]9BP#P%t}m2()&Du$B:a5AHnP%&Xs(ke).m^SMCwG3Es(oq<33k>=Lhd<36T-m6ISU*:0g2&M]PYY(u2([o>&dbJ6ya38cltw9BR-&+1nyd@$Ff!%({J$QFL5t)#eQwNti5a50<7beguzW5rw{tq6KF3i6&fp{cW.2dw0YW1V@3.eI@/jWiNo^Im)&EZBihy(15Tdl&)YD0/156Rs%HyP[MZ6od{Ed#35IUuYB8.S}I9}mv)a0tOM)YaI9tfwzjmSD9S6Ty4X7?<(bF]zXV}pYmljrM[.i8)c5amK&b$gmvaIn>jzp^>C-F)^3jnT!SP=jbp/>dTaI<{eg*H]Op}d6!PhBZ(OpYyj8pu+Ly[^*AdR(hEK@tGfrlt6([5:[1O/VIjfM&quuvVB?ilB}i.whY/TCK2?7o5joBKjckyoH>:^VM#Wvr9>%9Cp(cp8pjYy^UAK=w6tXBTzl*p{F]Jk+7osWB!0m-8Z/ThsjrQ*M!pD/cQmEN}JFnVMO!-Sm=:9i*19mpq4)*58FvdIH7U*05TC*^@@cBK}6pP$Qf[0.EI]zledQmi>R*Wq{z}YibqZu!Kz[1@4a?Sy868sUe/M=Wm?DM[NwG+4*yoddE{cH.QmgVy{wd@5og-k/)bJGYdP]UbCQs<.qZ{4&}G:iVW@S!Stpo{qn(ZKzxVesU/0tumC+#%z6JhLjj$ax>sVX:Dt*5ly{w-Ajh&^poVNs[[8!n5/WGJn]b:dD4xr+(KhGp2CT}2)qEOSkn!TmIEAw8gi+uPDVkX0!TXP$9N[[=K8Km^dgoW8X6x2@as#]I(y)Cb^AVAhu{sghqR3ZgQV8{!unXt>}sHzUnoi(QJtCHU0.NNnJ2$eyu$4dyA-MljkDy*d6O7r3fUsq.?eKMY/LZ${G*U^Ej%}#xkah(lGO@5MRSvQE+1n>P8J9>]xtwkZ-4vvu[ee{SC#H.{8l[iI^znQtUuAojXw:%W+6&9c.:}P!&fnMlXur/6RkD(A^k0X/G)Pc4Wpt*YIrTKR}}T2mauofxUO:r#i{Ac:K%KgN^^<&#qSV1?3ra[sS6u=rb1ohfdmt:@2>P5OM(L[PYQUc9?HlBV?@Y/L^]K1&Dbtde{oMBp<0-)OZ:Z>=B[If.ic7.x?h@a]4HLYe42Ga+iGNC]X%9=8{2D3h1ztyKWGc.W@1ojT4tw3.^8>+sjXBm6[p#.s8Anr5$&#{Ql?/ZD=dW7D$WhadS7XSbk-h{H!!RtxHVDu!N$gqcNtk[Sogh5BF&S({whXjM39N+L)/2ynj:^eIbA:]-QbpKV@OVjplY)CF{=]>@U.4F<4*$dHz9$0cvoR1i-wLg=Q{Q3>*O#-pM31LtFdQ.nVW8aGp:Njr76[w9$?IjO&B0-b{T^0^(yZl-6-I}C%x}7iL9tVx@t/*ZBTpyWO3r4Nfo&fLhHLYyiN7mU-mR5Q&4wwV@fgDoi+B#rZU]rfxaQ6m-)mb99+9@8S4r$kogn8iul!2^BK2K8U8(JpU@yDNeivo6mffkprezn(Z&^t!%TowbK2Ewr-wbQ+ag&$p(kZg=3.vQDrLoxR$9AMi6K2u>^wN$!iKg}@E>wGp@e!<56598b7Rq!&E3LG+62AgJ(c3[wR-euZ{>vo5BN!o1tu.oFk0aC5nTw-)oGG{x&4^s)EamW4RSiD=o0DSG-+MgmUaimiyM7bf!!fg8lmGj/sRQ@)qqf4buR3cXt$d=x:zyl[QybB#SdKTDkouJOZJ@aF6*D2QPdFq!IM>OGu:B1wqm>s!6Gtnd8^n.IJ{jaU8mJ!OxldQqx+:sh9$IRyCbJ5lgXP:D9wo2GoEN]Sn0P1YQybBWF1Y-BVF*6RNrIg4j/Lx@irxOU![!otKg{2%I12ry)r.E(p04t+F96/Di63yXFh#+p4$/(.czH%dNosq6b.B-wB]VoIge4uYHE&^#mJK5by}[n.nmzXIIzYtWJ0XV2i[2gg!bGT/dZlZ.>v5UwbxKtgZSvdl=v[e]zlcZdJ8f5DO!3Zb2!B(swzPjS?m=0E%+uZEi/jP#p{EaM/LcJKD/=SUA^QZpWoX2H[^INUZa.m[+YaeEXcVvpg^-M55N=z5t-H8o)JZ7/!8W&T@{28CkF3Yw.uWHqwvbzV^@j}#il5o-qocZ1:tRsE?)VI#eKIz7IAO1T:)YQB=T3BTl+-$N6rh5!SG!v.CLPdHps!NvwpH2UiQUn10m>FxV7lEP]Wb-xT?(lDaT4&@[I5o6VpFGvz4erqb5gRW8Uop+kN*s(nQgc1[c4H#>]2JR40$9OlOP0g?.De5kDh3uFn1#2]PYEH/$(t993p?^q>F:5[>6FXn#jZT{P+Y&QcU:HVJ=]}-]}FD>CkO2g$Bh0e)bm(y/%B4UE[{IrdvoP!jeS64@+QdLe-Yp>OjWwZ&/j)=!OVD{Mb-L}&+eP(hh?E4isDVhCgIEj7OdgU?#vz#enz?$?nrd{E$ekB9%.PAh>1l^4q@IV#%)L=(ohpOs+.)#&f$>u/a%KmSZc=LAd/gYx5+>$mri/+pbrIpx2Lc.*$@4y&fdSaU.z^?Nrf?kKrVn9}ta?-.e-{3&Phb3i)4e62S?s#+iL@$lezKP<6d7$x?4=bE=S=8nQLn@bSwM2H=bC2Hb-R$YUy4oZt5bFpKEQ%q7Cg0AP/.r$)jZ?WjqFLDdN{jDyh^:*3@?M>t}l+9/&>FlWXzfq(!yS-@ZU-Z*{$:Y58-?v3ONprbsd?7AF6+m@*IYz*{H8}Fr*v$#:6f*?i-bT:85cOtj/jK>RsAXyxcPjW}qWHHDbB&/K%LFpDAnxKR-vyHRXZ]rwZ@?Npz]:jaQ%q*v=4h!aythGbt9VkH*&TU&CH:u7l5*CxSFk{&5#hdIri.L.SQWMtQptYF!V*9((m0KuY+zJ[TXEBFil(CV387JiI[{.6Ok4-eeWMS}5hQ1[4Y8Wv>(x@/UuMCS(c:HE78O}jcAu{8=W{8mPxRA})*uzuvndVyP!o/>p$3T98XG=seVc30/wOq!p5Q:J!IPDbUko$ryTE#Ixe%lgA8J+*BA>]-S]k.6TC6c@s.?!7QI3BeJ1}l+PD@L!jJZI32xKW[c>Q=-{t)1AwRvYM>mhuOC{6g)K9F5BQM@T]L>>n]uD))%pF=eA]z}0SsXStl{^qey6V?xV>i&+}@syUSmke9Q@*RPSg}2e%96F?5S*@>?0d7k#$--:CEO5F]NBbtZAk]Xmutpbslu!E+R950jDaXsfPfs?HN}&ahGc1/WITV^-QKEx!!Od:!T30*s4lS=:4#m$}%2[t/Ij5Hl(34MK+DxzEHMR7:jf<[IEs+z[3qF!@cD@l5}{hSP+cBgSW2-rI@QG!8GMO4%QJNj$pIHAo7wb5$m@{R&C7bqtx27lbSxGSXOF:n+R}>x5*z[lIztzh.CVP&8t*:[?w1IY$IM1IKtfaY74N.+&q5Lh=mTIpjzc)V@{SSF+M(D03qt2KFw}CdWRP9ccVAK(*!2IzTsmL2[jW(wAvx*6JQz!&v]qCC>!j4F{c[^lWgEpNn-O?t6in^Z=J+T$qHl378CO2R)&(aw7^vk<7zUdf%lJSf/#!STO8raMV#KRQ0TZXgg#$LwMF=ZrEN8#NXV!nMz$j.cHRFGu3+3$3d0L3VpcnAkY3&?lTx${yjeVj1et6L8h$W&wp<[A:y)Um@P#)JK&D$YHlzs6Lg%:vge^>GNRR1QZ4y]zo>]V5N@BRy)oE{=^y-j]rvFTz13gWMi{t*3(ng5.3@Md2p$--WqB!EXu^:*oz(dic!xb38]9]u@BL9kxSZx6>84o5Z*}Vao=Ya+L}96J&9T}zZPU0{0x$cgJMmhi^G.^]tfRMi3A7h&TdS#M(>SAbx7&l@RYsNcIV2-]dCB6B.C$@4%Zd[%>Y$g)U2!^]+MB3jlHsk$uq4b[=zw$+-U7E=Dds}{ghZ}3Pox]wT[/)r>WMZTEAOpgk[zl=95w@d*l0)}MhoK65gz.51JcFaqBTH$]X7KDl17ZvR/e3?BZ.1baX.Rz{FRX]>Hh(2}$>udTkB}b3^hY@B&lb75(w4/vG=Zna{U7H$ay5(Up>5KMfkGg]yUDOM)E&5IedIphgweW3$)e-k%3U10cwJHC4w9^[0W?fR0{t>o78a^YlO9[PL2O##}K-go:vF[5Y5MY[y#qy*L3#pIBqkySL6-M5]=ljT5SYOi++TX-1VtHChQN$Yjcm*mSkmPB5<[Af)>)f@W}rLUSRo@]O?yrwGiC@TPc/Jt6}9?}lNB@a=6UyyZ8y4${7Al(G^dILcPI6zEsJT8N]hPH@*]bz:CqGin@]L$D&x1u.hk<>!M4PZtH%?pRVX]+B60=U@+{TSJ+V#nNz?>w<#?H{ERnIn&7snsI0yXiM:>[.C3]xekhx5f!71dlc!?VgWVA^=6k9OEy{Ynt^7+5m6TA9?i&s=KwUDpD#AeZ{edfhiea$ytdzooM*?7af=-PaQ1k*&3fsM%!E)p>2Xysl9dCPj^/?X/@lv4NrV3:xB*&+a0v-QpLR}TYfyr)!ShkQs=1/S[s!mZU7E(&n+K:B0g9S7E=4J6P.z$8XJ^GSm5z(yAV1TX8Uh5B^{m>K*KSt5=&.J!R95%cqP$oz}IRkK!Hrj$-Bk{5?->o4B]Q@TF^@si^!.jyt[>XGUUyf3va0KDMLSWS&U=J%GQ(!K9+0/CX7I7i0nlmZPl7hROEC1+p=NOPSq}Nn8)DP!TWSvP-Vx=@xzIX3xccSgNognJ39t[IiiVwG[(-C}NT8}-tG^.@b.ndUL*-^o*z0R}zl!wJ7MBUbsi=bUIF%S-!uoirfs4z5I}}>SMOksH*7Bk)Mb)!Lo1@-?VxNuf(>lMGrRwH^}1in]qb34x[EAn9wJR%Xi+ep-XFL/Bmi@t?-oB2U-ZBaW={{1uk4+J&)T}vJhdS{BgDFdg?nMju<}.RaBdP&?+8w&RQSpnD[=p{[Qw).$oA9TzuqMR!3gtSdQ55{@Kv6XLSvuqNJt+lpIn1[f4L7QINbP6gh]sz<52-]]EJCnbeIB+()Q#o&g^EyzKz2t?}ZO4Grny5AYd}^2][:@tG][)da#x.a7X0&^U@esIDAYQT&$(2Xh^$ma57UQZ1E6mwlc!WdJC57po!(-F52PE)d]bsw#7(q?/zXDytb+7Q-G/f40W$zVwF*wIawx)GI53xeZmR]NEqQGa>fG?3f6B$y^&lfuHRn#p5i{f)%IgJGov[m39a({:cn5/<}zi[4=:t6d#BW&wSP(}A7vU$lF$pBa?UhGpNMk%+):wH+72PTAW-}:pV2qwVV^!n6}$0#P!y+GGLnT^DOdgfM-6H)=4=]3iw6iM@Cz+ge3#*m*Aj!s=:9Ue*?Md*Bs{JC4q.*bmN7%rpZ9Gq#gV!5?mC4%wS/s!]zhSSXw8}U6M$T$XwafBA+U9+***YbPlyyG:Gh9Roq6mj/(Dsd7j)v>R9#3-[!jxXNj=s{WbNujHBtmpCHb{08l1H.Z3-ifx[C9!x$wl6tk=aoZmaYY}GMiUymY2m2{FZf!6c5=+5L2mFc[wRLbrKeQ@jYD-:H{cFZc2HaKav=4g(Y7UFLcenZc0)!AMu%{9F241%FDb:CgSxjWIPV1}5fkkrtd73K*+^ho?^XZ}#8u/#5E[n}LeMrh/@?!^.dYbyR]w:NkTr@Xf?6fHJiE/P6dPpy16nqArqe]Rr6-88M0HEdTXWsm6(5gmycIcllMW5!oZxgiq[j/tN8b1oAFK3@HYOH01J9v^G5#qe/78kxdVkG/@&@pemuN8Kztu^v1Jogg}k}-k78=BTMkRLlyTGEQr.(sf:-e76haAOb{vS:$EG:F6A6Mo0{cXf*>8!M+C^cAEcuAtW!qBKIcbJYA(t!S=T(4UW00]36kV-#cLC$qvi?1B-it{4=J[@cFD5sjJ5jNZz1m3bC)u!j.^z)+(s^MACHCETRccT/n0jNOgj>8{4{WiJf1N{]S1DE-A5DZ{kC#A2rF=BPEN%WQAe+eOV5D1e]/W^9qX8o-J4*7)WVPVIM)6>H*KHe3VM3RpM]ja%OCFkKNQWga5lNHJ&Mz@mztU}Xr&$Fj}I2-3]3GI9S*19y4KS5!(%tv&z=xT^}F1]t/FbrR0qQ59L>dy@EeqjzKbtyQvhdzohnPiCUIGJN-#?>^>yf6AB3Ob:10{nP)IUZp?ohnTGZ&v#4ZJl*lTX0=%/zaa&wcf}Z^Xajj]=0rKF{eIOpdjkJ2}-&)GC.gxyTU:h]?zJ(Aue}B4.n5:(gll(&jt5]?z5g5OBMZr.+X26ej7XI43-ktX7>P/!K0#euxGAal!A!l58e:ydScxRdP**gFe[XZU1!f>3FZ5>eMhR..!jP&myH/I^ST}^IA^YAlY1b}EJ4VS]iN+tPLy1srsIPXEjXb=5bl{0UJ^K+Rg^58]2n{q5pD=mm$9cg*Qy)}rjh1/5*1hSrOm6v*y&ZoUDT)m8>o-6}YL{bSQ1&9zOnYbiSW2K^(wO31]>yqrWxt)}<7:6eX$T9jyGZc0:8RzM0#IZQcY/QwWk*sKv#vPXqe=+udKC(Yr!S++8:5YXNz}f^SIF9NZFKGAEx3@3Nsp=dp=NQ#90N^mOZ6I+0}sl%:@T6B[+Kkwd?B+elXAoaUkL(69OxVARzP@o]b/AZvw!K!E0r)z2RiInC-ni*JGD?P$r2Ab4x07/ZEdx5$@jM8@l@n&1(u<(N77Wp%37A4DrMk8DS%zYFAuJ5u@W.z0V[?=f3aR8g&G]V$86jyGL8.#hy%6utP{<.(=BcrUjIuyM5YqBgl5GB*kvd+F3>TJEZl?dBTZT^3yC8bjoh/tTH=>islSAf+DU:G)xUXVG6R.vKtDWyl*N9JAa(j!%HB$W7vl+uB}rfLI^xX(H5Pw/s()8Kj0M<:W{.P}DSM(x1-ALu)/.Lx(VCG+ZiX&=W8]iG2E*&L:OGa91uMB7Wu8GUt*D!N7{!$z.#oGW*8o&E+g%ZmcPxvf{3y{bFxF5cs&d?^KyE4-&xsYcSbgz:hqzt.nm-li#%DMn5F5&(0viJ>4nR+N7Kv!)E).J(c#{Y(l8V7kBd]W1[Uol+t[FH=KtAnEa}0xe{7NILt^K!h+gVodW$l^%g1(w]T/$6}e/2=WhH?P(Ncw6}y*xy{?o*.?oqWd*zbFdH5$Ga@1z9A#+R}EwTxA&Y<9r^z.eE[5#SShGHn5wH9=]Dt#MuU)RL@oQ6vFCXis2{oO:gbTE%Nv.HCOcPvpeS{4*!)jYdJkCorAD9{UXD:Q)4vy@*7k?yN1cd@I6PcJ7}6gQfF#o<(}VOpO2CywjeQI3$}8i-NdX>uChiOt5]wo&pU/x1!a:9vyjPprh46WQ1mspZWSjhzpBGbJvRS9UmA8o[tYQeLKns+WHzsxkxV)g1zEPfOF{L:pstfaxyH{szSIu+f-CLEyM(Rk{S[j#Zw:A4JD7-}*e+QR%@5(iZ16r$AAxa-m1}/yZFbIHu-84c>y.eFps6#*/1GoV^8&!f(dOPsYS{O]j3j[K&QK?-%:Z&!bjqT^UFBb.=T7w{3L3Y]#}hgmO[8w^sb.v5JeRh}.mCv)=AOjDIE{i:7Ja5d[>V7@XXprtiiGw!w^C!)o[ufO.jDo]XY2r)6DZVN)8Ju0*YYSXbQOzmoc*7=:MW^>*Fo?[vU{Vj7Xou^9jyMoC6s.<3H{Trx.tUH3>HkFR}vRsBpT/Yk8TIQ1ClefEUvXT.KHbxTZ^xI=@RwH(WxH+yTe7O0sV[.Z%?m/cx5jJ@a)pEFD)k>i*[RB4YjDUqO${cKZtZ>yz%sU%f}7?:c}xk>FhfmzEIRW)eZ3@UbxG*]S=He0=LG.l5Lc!3Wi=5n3G+&0{)yIDVSuxzZc3{Hn$D:vZ4uF{c$b28k6#cx>(o}Z6$:Cu[su-[pYg&G4^QskcWR*SGK4:*Ka!P=&*LD)?k6/MZh%ZhZK8n1{!/bW/:%z@^x+nVMn1/[m&=*alK{>C^qPNqBoi{QFuSF+J*nQ4.s6qbZyAPZ0g-b[oxWG:/m+>R#wpUvHdS3]S:5&EYAO3g9{Y%Jy<*RM)Y[dS&&xOC}TDQPA&?FDm*E<5.}Z**mR<=k#f.{NX6PG?WmA4}wI(PK1N:=:SfBt71o{G5XVw=0E<(y1y}K&q^]W}+%I()FSXCqJNP*7(7AA&xEfqp3AS{0D$.5PF{e}:.d-40/ilY^r-=(A[!R.z*s(M9ZzU!{r?HWg)u[+up7/sL>6yG&dhp[*R!OFcM%=8gR}x8x8q(rGnGRoXM]]?*F7J/vW29NlCXK8evLMP[5auoo{%p7vu)WrTPjjO%H{%U=H6}mCTs[LBnQK}y#]E.Q4iB$1[c/9mz6xe@h7f1Op>gy2{VJ%mVRsf{l7)}GYSEQci.Y)tNrRCk6Mtxa:t:AGwoP#<%4mgWEgVrlQF%SRCpZd6)Yj1SGR*:fB=NX.+JbKCBexf=!3yR8gok0[cUV<3^]g{jfVW]njR)oJCXO>cXP9Q(Roj?AgB3Z22/h?U&Lr7oKBQD(]0*%@^S%1?5[6#:?]v3fO?HjfSY&+$)x3x@l>{km>kU]Nh2dE54H6>0gt00W[u04KyegBUKlxXC^X<+(cmYC(d-q[O2>&W^h0o#J&n?]%N)0iP3X&U3[vhv8>GCV:gzIeq=R6qaJ5w5EnwG7yWA?t^pvsd{Rh2z2Mmyk6CgnjV(hJYrjI/oO8aMq>mZm4#H9nniIT:YK%XF*L4JKq0x}$c6=HYd.VHDVgO1eA#A>MFebn+4A=CwyX5Xj0xrWptmgbFJSiO=L3hVc@?VIr9ws6:+>Ik3e9I0qpsF2NGd?sMEJ)(x]#zA%jf8{[}JU[q5fJ}grmK}=)1eSkW+h[F?*ky98tV-nU-4U+S8P/caeLd/t2WRhk0WI6(/RTKPRrCm/ITp.G$eN<1Z3-r-JL[A:LA}7cO6ris(q(oBo-O:[8zg.ctdiAFsOoU&u4&dLLzodEf6bz7R2A=#p%8iT5HN/b-w{T5C>:#@/IM%/7TpZLJgJ{&X){1@DxKNFe&hArG^Z>hX$p=vG2TB}=2JNi)9X5zGUb)GQdp7ZNjY}PG&wdovSZK=YvXB^ndr+9SqaZD%s3!:jlse@&GELh=wGN*f/YMYt[a9?SYAi9W5<0Y7K^AuG93zkGsiC!{n3tXaar$a:[LyGZikQi2NITXfB.A/!&A>HPbk4AtIXmJl8m+xgFbb?SO*L>(U(j9:SJO*1})d^r/pMLt6Xy!=K&Z:oN*Ac5sxEQN]5=PS*Bug27wuPXVW[S6*:wIC+CJ&Z79IDLDS@s!&t(F:!IZ$l{@AfGBvQ30x.vs$omKh]WxF?&Jeey(Y^[!E[{ys!}Y}7l(z=}Z&q67s]>)G=KW#)sPo=d+g1G7>&*ihQ&XvO!W3E2wZ.x&/DWUp)@CUgy}RP4(mb>9)V7TZyL<9cTP.Pir9tyEEtjiD}T+Iw+crJsk(/2tqxumyd.bcMSnXOX>48u@aTt.gXVCH[$Wg^jC/i{uDnMx5)<8Rf}m]ONhe=glG*als55#bP4ze&kYc.!#G2yiTm6:=ICk*^UCO9pi}:6Md]hm!w!)YUihSfr1^kM=x/Zu$Q2n7/Dg54y:A0AL]tKso4}$sM?{$5+uVzG^U8qIQExVoeL^Cqf(pYt&2NPw.epwd!m8q:xDm?BWYFgvb8}2vRujnLcc??cZU)1:JYB}Qy.[3MX-qp?+th]&h+*+/*DRd-K1U-d^Ro4eFv%zS{uh%4QW]Gjn{b$kz=Ob7Xj5sEyKT+o8A7L/XLV>xPrgiD^.TsE>IN%MP.PynbUww0op%t:(GX8Z!lT5@tWLr+7yvOULaM*]S1h+e)5@}^eis8!P{uKCaNpv5F)K1=s#Elx5ZpXLeWwu@Tpa9]vEDByKDt#EPC1Fi8s#k=p$ZHn!XKNq?^Gx=V=pPpztmbrEctOXopBY96{PLS+(/LupoyG=Hq*$ur*[6F&BzCtYbgTWl&M.=fa2>ItI(Kj}2l(!N{VyapTeA@Ey:-e>.^2WHvqoksQiROYdGO6S@ORyRJb58Ow$Og5R7*hfs]#:SrH:L-Ro6R5@O!DequZ5MOegt{u)5jNM(D.so@-fG#WhpilaBkYUYuQ9(*LFq?rkXsI+#^ezC@(Ud^h*ZQh}(q^b8s+2GKnbDXqFM3-T=1I.C6js6Gs$H$zHm>Y{3%1s9[i!C)wi=QP&}^.92gvSP7#2Oyj7#A2T7$GE7o=%{T(X]?bqc/JkGrkeL(AsL>QMYP[85Klum)hRmby!WE8j?ej^yi%D/-dxLYL$#k9!v%EGwTG=tRW9]6FO7)wZs5u?](Rqg-fOLO:b3z[l(w@U%)3)+9=W.r2P6eQBJ3==2Di:XGe$9HuLpYP[Qu%pgo+vj]U%Rd9hrDG71IK3IqGu]uV!+J+=9CZVu<(I{A9t(}H$)[yYLl=&/]zGozfxv/Pqu>xJw}-@e*S:[N*c&+Q=U}z}p&RDLyBExK:zd8x&Gng5R)wMT]4&hm7A&U#ig6uo?P2^x%g%4P{8LX={]LiSkVx&c}dNrEx$Y^Jjytgx9x*NzNL:OAPL}BOyD$tDfqD9au&]dX@1Np+&K?Z{XJ}t>OKN!ldiQ&Iw&xR8-4Jjk7[W[eqURRgU7JZ)BSi@cplK9&wsE(os?Wu[D8ctO#>(5jlVm&m.:?9%}P{sK.h2LhmfOIPOt(YIQ@6.1pcqp[xQZbVFlm[=9Wf961h0saqz>)+#HI/F7rE&/Q>7JK4IX3q+Yu&1JfqTOIDHT.hZfJYD0q.O.U0gYvn]/EWBtZojgspgKiMnLP{0CD)$xVpo%%MDpp$a)h49+rpX7+NE&m#YeJqYEFkj8a)rynau>S=44}&t[52RJiakJE>u3l>j:#:tutF8HQJ4Jtgf0fHOUl!5WFq3fJQs}?nKYa-&z*k?!0Zn!AsdlmqL+WP1OF]x>9KSdj70A8iM{lim)F%eU?+kkcG=2G(xH{GU?VdM&tp9*lEQh4Yb9K0otpY{X1?A)UXayY77kGy^i(49:u@&fdR5*/]w5[H1h00ic?w@L+pFFa&{)7ry:6lhIPE#v}9<(0CHA=KpM2+.XOuh7/-p>&f}E)ISAw:KFW&lD6cSc(:aGORu4[DXI}WNc>[T4XEN:G0U/(f>fuU()@8utUo5lM6S5RBS:?zUL%@SZ3tM=Hc]OYI#9=5%k({fh&@jd.6HUqay9D!!1OUCLZ[4uWTHb%Ivws8rtA4J]}s<^uKSF3phpQp:/(QgaxuFtPUfK8)z2qj0S/Gm4IJKb+m=UgFf3/c8mU9xnN1*:u+eCZotZ-k9+ieL7lwN$x(djI)tsHjk@tV*iCxZ?>2qZt)#H$66YfQ0eHU)bA+JGRUVWw:L(@rP=eCDEhO?y+Ym*?dE[F5}W/UAn-aTu!-n9U[JW08LtP=-bpmkVJTkK/aFD73G7v-/8[#]2)OKYjN!iil(PDCkC:F]7l-7@wML{G8iVThl>S=jDrjN+GgZm4PqT!$]X51h*^?HQ9pLQD{tni.>eVyZUC0IFJzMFun1-Fw@dEvP(oQAs%5{[QhDbX@]IP=w%8Dr$2Lvqht7UDmM}lPfpmxJ(Azm!$bmdxsD/WrK*}j]bSbmded[tXTrk6WuyaX/Pis=!ShZ$L+z(To$^cSn@uN+wt5U4dhqyESn2Fo53&D0K^n0SGZrjnnM+ZB)@&i3N-g)SXk!Bq#PLSv^#TXj!T!W9fnJV^Dj.&}Xz)zb4W/>]/6*ty>eVwrhb%MSj0.EU+cgEdK[&[LmQ{/q1f=9qtytLC#v>(M:c?@w!M9#?sNO}LB+2uO70zZ={tFHK[b0y1Rfd8@u$MOMm0#LVt/yxtY+6Ke[sl<:ajKMguI+hxoAO-(Lacep7csav3OeeG*C6xxOhYQ:bvPWP/A-K4Wu.HVLp!h:*ye-RGMNX8D.Wtbp/KQ^D>ZF)9J@o8Oow?9ICLi/PcTu(p}Vu.5mCVO?Rb2@HtK9+T-k(^1ekg96F0H(BKT$Fj6/dR[(7AO6^}p:M6=dp{J}:FJ=0l+dw1t0l2O@Q7s&Smg0kLz+VXOp%v0PSIXM8pxM@pFr}zJ-^ov{45@0d)ZG0Lu**=Lvgzk21Z$K4aQa-Lx471yNO3RYznJa8[+(Cv2{0MR7n))YYd*8QvLCDP2=BXpq^tz?qYtbt<5I#orE%]33cDdB===te{[SwxWS5@HKGkL}p}O@OgUv+T!ljXyNlKZuV6^5^0MukpWF?Jn8R+iUW1l%5(]O=zml-K67(*:v4r]dAYmk&G<*:&63KRFh:^!^1uEZ!bT%:JVzKAN{<[V/0Eqi#E^Xn)?V6jC>qO=NH/^*PF&RuA=k=7iqW!hY#gE-6W%td@xFln&jR!CP/{n+i=Sh-#6v(p$B(VezWDV7yVk%8$I1c?6[xTBg0D^Q[W^OG(E}fKiJ{=il+q}>5y}uSx[8RAQC28eLK^UKXQh6cZMDv-t]nM0(es1/r&@[HbrfaiC3wi*I-k]RsN%wzD[?5g?=R1BOGn%LbWG*UXR>o@6zGHRyiz[TRrfgrIQz)p%N0ugM0GC<0PWwnrPg7AsHS{1F@M:.k5r5A{07G3K7QyZbo>.T]w*bO(xb(7C:er<4us{pd.*9Q2v3Px:tEXp(Iz-{NX0@/${HOWVTxmdY-?T1>ues$=7d:CWTotI[*>!8j6sKh7z?dP+A@ldb8ASDGMN0y2s>Rwwe*tF>4#=FsUW80{N(&-i(Vv6YGBhX#]^OLpJB4]V07kmR+qz}Y:0FcqT$!=ld:^CIpX14?&+3!wP-=weFgJh=l6I1a]Tr4Tyg/TI}kQ(7NYZlfaF)/DUgZ$kp1tSGWRp3d65GY:u>Nsdj?}kX8TUW^/?[B(4ZM$K&s?zYVN/rYK{onpViNMU4=La*f[r?t%+/R^??77grPjC0hGbdP{=py(UY:c5=HY7Bi.D5IZ.gQ+xcJ}O>Hv.d[{32N@u&n]LjZHA]]=t1@@vv605bKpvtepYTl[*B5Im!(vscZn-[x0*iOeGM^]YHJ:.S8&&)wr%)b+&K]eic{tzJx8OLIco2WGUITZ{*DnnUyaFp58u:r/vxzUIg><$7JVb3#c5np=tmjZmf6Xh[:[3Te0y:SM-:Bu%mQ!&@oJ2wLS1EtDzsht9h3axbhm7}Ttq0DZzlAvxjGpF^/sTwIbANt2&uWy4R3){k@9()Z9WuN>@HkKT8W*FOpbn{4M1?ciK}4S}GYiYQ=(%%fo0}q@tls1[!PYRWy{oE3/OSb-@YJ^{{.yXN0q=8]]Y!bzbT:w&KR{lu[U@+*Os9.Td$g%VkA)-dS?dy8v+)D+cN=CA&/8y=bT^1&lh]{C%xFlQ)i&izXvu=3Mu8tP)N9:e&uz6DJ?xN5hNao]]gxaG!/vMsBez>W8S{s4Oi@T7OaN7En!$?Sfz5xV@7GUI+97IOv]DI}HJEBE^a9x[Lh99*Q9VQvu[p[dGy4Hpk1kW@*2bnrbojE-NOgo0w*P-p<(OT*Q?6?WKN]FojP:}:H*$iEo/UYMk(uoROT/t{zsmhOpt-H[TL6AZIlgMocIbN)l5n+phd=cs3tKP}j1*PUjiHI{%IMW4nG*0X*9ilFUfeJUF:]Yh1d^6Hm4BrD&ruhQix[DMz8T1tsoUa&]by#1f/C)?w{1c=ZF?vaDP).L+VFL<=cBp0-kMY=x9D)E$VKr/zCF5I=0iR^nFs>>k/@C-bFdVKAV9AQf!Bs2^[v)4kF!uM)wFlp6}h^t7Y5[AB3SG(G6BaMpz>qp>TO1:%pkhXNHg[/N8hJoR6ryZ20wnq$PRRbOdND^Zn2gJ8]CuN5BcnMRZq!.hK>2dhy!m{GJDRFo7y6oy@NeN]wIcV12Zim-hh@xhLb.6:N^j:bvnwtbG{y2Z2@e7/]W)1:k{}fo1-f@x=$QWN93![@fdML-g1?ik4]!5f/6ZiWElC^0F9pUjB8e]h{lMGL+NY{$4]/NA8*PTAT3s<$Hf:aBxp^n.zYIC+./VFbu1+VCK^^CgYrg1qyA11sT5dD?g:Ss#p@s1](^OmT9HtuRCPRlv<3rE-OxEtfZ(/f}^gRwOMtk1}P=x3&IF3G^Rtsp{1WU&ir&)f1el?PKO1GKi82uPS5/uPMZRy8P?K9DCqsddI4e&B*zxbpx*v=78#XvvTdQizxfFO})O:EnXa*ZMsladiI>zWuSolQ6k]w!#03.bz7jbrStN[H7s3?ST+g(s3rRP/cYxACSj#Btf5oM]uI33}2Y0=V=x[E^wovR&^R%Kd^Mvwu@=!V0C8AF6#6@)62)k/y7v{8INvE(Gl)0p3fR1]Tppte{ClM>^%#}V4.zymB.Qe0PknyC-zda(NDazMID.:Ld}CJqE*(fl92gDM78Xo]4SPtyzlOrDNW$zyBNHBV)Uq?aX<[b1W3NAA4B/t=m<%[OqjOg298QKEz8>gs+xctN>tX0WM$e8RvlcoBg%0$^0&*^!@B1bac/++B+/R/LS^v>L)9wg?r1p+j92@u!6gSOAy3U-10dw<7f4iGk7DI6acc*TJ%DoR[8Pwrwc.J)Ch%jH=@xkSEDLtdiq#*[uSpa{n956cSvFEIsr[+E1g@$:Qq?3h+]4ivjMn$#CNG!>9xOXA[=mL:fcewQJls]PfSqPR5H#^S8]aqFe*fCJLa!f[tqXvpIyi+T3Tp?9HeV$XeLR22q7mOcS}Ph+)bS3G<+fnR=g#n2fm6uTJmnGJrAjsBcK]^pw}w6>L!X)$)p3Rr+?t-?%!k&92isQ4Aw-Jer/2y$mCU::AS.z99z/vHiFqolu@[TfzAQE)d$e^]g>vSTk}ZBBf(}q>W2cZ1+L%)jsF0>-[o:>SM@r?A@w[qm4Ydh+b{-C7jt=go(oRD!ulXOB}]@.3K8OLk>PQ/cG:KBgRFqDZ)OGGrqOF%Zr20gUvR4D1go8nI=Ina7S885.2XPCx!4mIwN:nguiiSy{hgqA.6=i7gm*{(wtmMpGs]uqG(F9A4-.?ALMtx<(&TR)RPKh37rdm:8TFQLz5l:o}XC)9:(?D76LoIb*UdP9u!s8bu>?U6pLh1(x+>ZKjn{gZD!:+U23q4:u*A=3hO*h[7Hr[ZEvhH/+PDOCu[0?ELg2oYqV?D>:n0]Nc8MKRQo!7{T=[D={fKRgH&t3x6NaVVTD?WkTK/-j[BF^&MPZIh6}t17VNrF+m!Waa/5$<&Mx8Zp<>>J6&tx}jDlys^5)&NUlL=FMgy*)0L!0lk?4u*H{j@t1j/3(2=(h$TFL?$CneE#MU}ubLG$XT6Z@e]JFhU?]p8Z*[RcN:7ymA9%h}&Mad-3bPfq:*=A^u]y>3m0[#uIVj}y$62F+b2&Cm)GoDx%aVkO)0?TnnF0%q4J^V>H4axk}(=m-[(NrA$L&yS%+y}DN}DFEtAMM{[N#W(xMiZCLTEHEChtQXZd&E.dREhEWEy6-ZE@ILIaBl@Y9OkC{iJ!RmP)O>0u^?GaBvXKfu^7(H@gVwZo.#4K9G{47L}=Y7HJjU}s&Oj.[V^gkmBel@8rOaOBpKXq#3niUk-M3l+F{jyU?#]&m9Re(bxvVQQNO/Pa(N9jr9/9?Q-nTu]q9R99:ti4JMbAcC)nk?o}9x^j^XHsNE(:zaOT@15LGpv5CxOkcsvZnbFBG}6ny]bF8]Go%CQc]Ahjao9@!D/3Ug-I0*Mho(wQ7wHJ*]12r?J9EO=w1gz#{Y9mdaE7EX6[eG8F78&){qMnL.3qF5yxx8}9sryCA@Xsny?%KV^{g-ZQC@M/rZRUDdv&w#{()6G}ubyfq]cAK>Pn}lb1o!wWA8Yi.^*]jdnGfrfvFj]a)EI*LypDpc!dyw5g!y8YmEea2qjR*rBUObJ5l2X@pC70lk^PU=lr>lD435)lU/2UP9UsbbYR7QYT-nX6=vEryov#/>g7f%t{^Z[+V1fGCG8:zLBPN*AFYdDBY{(}IU:F&9Y/Ju]x#n95ZMN54}UDxZ^<]iv+yB(r:m3r52.OC=IT+/x^#5BEncHH@g&IjO4ISh+6K>jGQu}dLG$-X}jUV(?Zb3[JA=BKtDx-QoaF3ZLL?HF[pF5xM8J]&)23v}Sozv*IIX#Gf{BswDH+pVWb2Ivm{12{JLZZ-%mvoAR)Xq)e&QYHf!&C8vXJ4-l+%!3&l?5h)bCZq]W-D?SPl2!]I4m^-6apPy(]CpY8I!Vto2GNkT99hG&f&&3-@uX*m.C2l=2[?ig%{tt.tdn7M<+HeYUWR&rrZFYtfhz?g6R1Cu%h3J{T*7T]Mw}[DaqvPRx8EtW0EdE&QWNIJdTu7lr:8sc{K>ucaY$)U!t./N%3FSxZOdpcZxdlJKE.y[qZUGA]zR>>i!Gyt9vmf6Zo!aK)Dl57CdE%0Kx>?8AIp>.?byYf&gG#gA{I&K)}p-gnj+x:*!^ED@:^4?8.Say)-j07$}+XgOqRt&U.1pd[6xX!VX/qO}Ncp7U=ZZuZ@J{g{x5258p]-zr4v<3M&.zp5TB+X#8H&)fUXc1ZlDkM{2TrC+YR&RJV*U6@D?)zf=E(zaFm8S&hq):3DQ+a{4%*(S2SfXQ%7R=W]WKzT}v0u45hx$S658e06-kex-+Kc8%5e%pDt*.{)!i3lbH-u)4P^}UPrbXur/!6mGq0pj0hDMVQl5y5gzOvU*z/b1cy?@%xcNHROSZ%QMd>xf=QfHqRhr]Nx$+&STT!0+U12i*FKGP3Wy/Gja-.FSOB4N%TJLe.&uJm1c5ro$*O9}hR(ml5L)>rY4pLXKcm?nEjk8GP<@8Bzb}jftSoY$bcXOqJ?ZAe)?#:aR&I5ibf:.0C?1)P%.zbgzg:pk#V{>pw(a-SXo^@.<44hZLS0YXak#uVByv%kN7jk)ny9d>{2sQt^TvgbrsdmvxQ}BT3A0y7drO!x-iBYS}XVN3%TY?V-NJ[.7X{a?>BgVx}*%LromTe@DGioM*Ka@aT}86?cF.BsS2R!1)/n#sr.*#.6rQ>X@#@+QFZB[tYX7!wMPX!u4q!0fc9y/)Z7Iw2CNsF2h]NohnJ*]3{!Er?4nm{+FIsiS*v9+Si2ltk>h)IImQ4(%z7lyi^wLqoY+^T#XU.0TZ<.r0u?xjls.kd@4$dNgn}wr!oXgI@i((@UBafCD{]W8wnj{YA:pU90]^)Z6h(g]5HKJERdUYPw[boSTbq!V.4?^gG!.}9zs=bQFQ-TRiA..oOr&j(IOsYT!p*Tiyov=h&Ed=Vl/b85=-OCT=y*6<*=sJQp+H)2%4tFik@A/Zvpx{^Qc{.J28MJstv95CK.Bo]!5Qpggh!*+4<}h76s$F+z}&:F![qEZR=4i*1w2D&xRiQ!gUO*)xEMrHTv}QgrP2LFHP=1SsD+mm-CF}4bzNDtezxo{42bKR2V4sL2AcoS#e1q?eGo9(In+]:y]M%@Rm9%ESWS@rx}[5}vJw)?+eC[#QzEkPLVa{HBA7u#Td0dew+%{[IRHtQB).V$mUXh25p8l+or1*:WYeazSJv8MY>RF*[yxy[](S{S!]GZ(aP[#5jMz24nDp^=j=Y5DVUTAd@FF6sQbUe=%FN*F1<9IFQb[{LsgYn:aTe25k$sY]28iv3@%]..M=mxo{{4HDZwBP=iw:nuLFUo+(lILz^v}Q=e{#^[6ie0]}FU)AJLL=AK.n[@b-dAtFhQk}1!E.[jKUg{XX8kI$E>F+8Ys^KP7HrnG9=.*aiD]l/+5XL}1#zI^ny%U:Vy=zhyauULax=K1%+uD!Gv=2bD:BVRSpEvl0T!bmBQt}:]L%gVOY)m7lBH!<{:^Xe&]l!q2%XK#9udajZjx(MS=Pje!e9m-WenMLG-bBBJLE/!}?w(qf28v.0?bQ5)m71M=>9A%Xka)tk2Q.UQnqjFv5M)TWr.):uIjimlHdAW1x++Nm4#WSBtLWR2T-:=Z^C0Kfflzs&kudPSM.a+95Al5q!P!z8<]icUUgyUx.#YA+4{h2k+8Pgl1W7vNPalI*%O2AjfSOUaABE:!zwgUNCO!D@DW]ZQd}*Y7.K(=Ften[:^yxM3q/BtL?C#s#tuVuxU)$bg?5D:1OLtg?i[X/8t44QuO.%YY/aAX?LSsV>A(H4p/&k^4Y$53F1#kKncG5[v[-U}(}VMn[r3LPu1R2GFW2dL/7R7/EXKTh:zNs-6fEzeD7JZ2]BDcZAp73+%l[ljT2K%}DXA!dX+RWpy}tX&u$-ijQTF/jz[ZkWg!N..n!zB4?BhD+mNDOi4XKSW>0{rLi/Yc@^[*KhzX3)r1AXg#uzsPGoSjb2:iPW#{M#ih5:Jae7V8d*[@nygQi*[PvhIeii?iWM-C9AycAEW%)eLr-:V#Q-TCfSy/pj]y}(TFZtBD4GwV:tQ!u2Un9}07E]}0KVLfsfW2/-uc*MK%c80LA*8i]t0%v-vnxRBl>X5]7CB2OLdFoV6I0{(Jq!)AP!goL9y*GLbTIxvRX&9i0)u/ijzxd6)ej/k:voN)&iB[%6IM$oABkOsb-KmNkNngU^>lvjU/ptsmX?Ny>f?gCP@K*slP?/yX.eoi%fnjv[yc}EM{By--UF7$.[W:/0dL>dI[d44Dnu/1-$Ax/ffM4^{tNOdQ{DV:^NpRnn0t38*uOk5HiW#F$w(=S9tmJ5>jv)YItJk/5SD-m(N.([wcOue4nuGl5H/:j14lBQ&q4iso^{9Me]4GbddBm^5+8qS*s&[lq[+$x/mTL-sUKy#G1qB:IMH8&z#{.!aT!X[(b]4G(73}eNn(^=}+7!u*n:+3[tyqKVh}x{hh(Qdfaxm9qM0)+$Z@DEVa))@iQgXKs&-X}bY3:SOn-L/H*7Y#$>BQGjl:-1z1cc?n3feBs2BUm/CL{OROlaNmGVdC1gOh[&wAiap0xL?G:V%kzwAUO0.:$>}yVj%TE%VaE{12UyT8j7xlaM$:F&Q3E}nRF$jtGXG7I]ciT5u7>xX@Um(1F?KjotTePa!+PpK4#S/a}fHV.9c$.z]=mChMhf^xe>A31f1/sM6Tqm]p![kFKmhEmg4FqQIhn?fPH$!cF@!0sEaIVG=g4()xt?>#13jpQ3O1o8G<:aj!XZU2TwdLRvH$)8K[pC>kpJ=HnK7GxiTm2w$09c&Ue(V4DDaA7-dnpCpzt/W3p^(-B0L2iBZ%@:*3l)j/gt-Xzh]y2.E2qkOVnreSl3-P*ahTb7:8}T8442SkHF6Y8:XIlLhnIv=&M69#Y3-f*mU{(4:2O7qE1U{=ulz9:kh$4kLh0wzlyEeDOko:gwF^+^9E0J(dG475)I9r+7O!.hFr{x+12xo!}!csFQ:wgtO:TdIKdO*0toJMj57oz09yb5vO1d4I2ZL<#3-C@Nd?bN]PwzML1EXGYmKWGeTl3+6mZvXQV>2Uz!c>oxi=2aAAqcl)8XT:dsJ*HyQrycMJ:zjL@Iv)UR6%ef!qTcaoR!D9N!y(/ylFny]P@ckDe+DQJjU1Xh$AsC0JRyJBrFQ-SGDn@@HWt?-#H-1ykX6-!d#LbgZn^Ck{Lo6.i40^bqN+%PQ!&SNB5U$.lfR:/$Ockr0WT4UF#GdbGqo)S!QRmDo4#]-/J@20j@S]LZ#e]g{{y#mCcd*qdMBxlQLpdD]Nj-Xhi-kQRuks^i4kAQq1Z9n&vDaYR6AB?z-S}IYVE[raQHunNo36C^JwC/@olt}ZqRl?cQUdW%jP9(CLu.(0DLt)g9li^R2XN?AC>CF1?A}tiH?vHutIg+zqBN*5//2EwxPT+sW#m2fI0y&G}1s7F!o$A?+TMs&dlSwTN1H3-VSKW-k5c!C/DfzwoPRI9RPWEyn93.kQoM}-)4p8zq/(EQ)HSxYM]DDUX?L+t4=ZwtOgZ8P]tAUQylxsv9aLV}}]MoseA2mOoBhS4r-.+[nf3B?dxG^-bl8M0fLu?XQ=9xUqqJLj0u>p:>?+]gp?)jzy9!Af[kt&Q@k!:CbuQG/r1ObK[)LYd&#Jx}[!V:?2QKy2K@JU4th0q=u9T<0$x*Jmit6Px1p>6B&WD-.*OdA8Mgl@TA4gaF7DJ@xe&[XvLSmJ}D7jceMtl.BK?m{?]6q7@-9W^ufH*gd@?:Xuxarl2OScu$0WPxDH-)b?9MO7z#IhBL-@T(7h*YOVp][D=]uLLpaI1fZL8q%uv7^+6>BLX>3+fpYz1{w=z<=+MPWk=MKNQdQp>j@9@qCDr8IWG+OcbZke6Cj1wZdGi(IULm*wu(Delp*[>$iTUd7pofldG/IRd[s3XyUymMo4og{8GWME[FS.[2Q+[$uBw}!8X/ps1+CK{8BrT2Ai)R)7Gl7vPA>Dg&+E%hHx%hk@cDL/&O!OnYhT{]7v3T-YFO*X*N2ozRadeNAJC[bZK>(WA!ohGL5OaFg&FbWo>o>]YR{[&6ivRh+LwwS7V@NqeJ:FEr$(M&w!Wa)tNdW[Q5S4a=44R!v(J^yXD?G{?UkeP{{JnMx7OT2at/om+EB{Nv]>Gq5an79v-XU@Yt-i4mP^Zo:7f^A-y.Q+!:uo[nA(r!nH%ZQmmrkwm$P**IvTBdFw&[COrB5]cLWzZ<(xs[)^LXP<>wVVGx^)sX/^HydD?3L7Aesl74hd{38&x=4+QT!6Zc%-%3>g4CL?%HT&tI+Fgqtyr>L{7z={Cr/gm8:AG(a]p2Y]0WS=1nke>^HCTkn6A0!cj68b#s(0dXwef3sHKHyLU+k9{AJM8!{YJDCHmMaUd+*jnULVUBE)t-?j+Flg?GVotwFE93S^dq/A7&!q3&a%nBm(vpx+Y}RZ/M7Cbrl4]C.(e-gIt@!)&1U=Sk(XbgLx1#?htXy:{nUD6O(lJl$[Ewi>TfOtwH(zxuIg6F&GvSSyE1oXTy*}B?}[%kH8b!I3bY27(?oTZXf][0GD9}^=4igU(DN#X2kgVZxXg1RqEsF+7-K6(88Cj>>XS6BVfrp/-]35u2{{igPL+$BI)N[V9zH5Kzyq/FOh966Ywshwc$wx-+RB!2rGTLB3H>f<0mEdMoy@Pi[/XD0Bolde}CKrmtYAe:RnHaze]!]9$^hK.vaT>$Vl:df2>O5tqzHA&qw2jswIkDv:-54[YNKm*h$tn#JgwLH9PUH!4Mqk&egr7PV+.g?.Khv?O(8{h(]/Y)u*o&R.aNz(7ai]-B3P/[E1RkVpHseds#A:%&RU[Z{L(zNQ/Za)9^yIb*.9@d*wC4a&lK^hT(uqc#zDPaZ4L[hgc@65HU0u(iu]v64mK46MW{/Y.7^h?LMUv2}x1V2)h[zE5k(-{SiY+&uvtygb+P^9T]d}vxM$t&l(mJ1ebkyKmoDjE*/QnPm2Z2E)GqUeN=eM2cU=<}vhk54Hm2E4a3pH!wI+7ZwJwW}l0l)/@x}/Q/8HbP18/&-t)]Jr}Q39YHwIEUVY{UX=Ia9T]LchVxouEaJ#hPHv%G]EMMT&6Q8:si^uy>jSp7taDr?i0(Tj]a9$]ZDR{$cH/1PBU&13):BiNR$baap@I6&uK&s?zNtg+Qc>F5tnUHP{0Z(=OUVz&3$lVY08W.%3/mnh(mFZ?@hwkqx+Mx5GfbY(3F4a#p$1]gwN>1c(H+V:vR(.=@=0IICpq^+S%t{-uX{rM=I9lj:aIkl+aZEr=i#*28zArMjWcuzeIe8/=@a-GntTo/RxU&VOZ[T:h}LvNb=[7YCEPXCJ?z]!xQN&xatv5tU3IK{mmfc9}:CaLSp4c8d!<-+{dfS8?[1=nF44g)Vg?(X>1G4/NlR{k/>s{0i=LADc-B3HtvBXtDza?.S9QnSy^O}Njpy7^D)L{$yBh%2c}FB3lG=isTi2c{7>:#s1}^$*>66n{l%B2*HTAAknuqTZKxDH6mpCs.x/+rMoKew4/@pacNP8ZyjXkGQym!uB%+J){0SkOF>J?G?pI*%7%TxmZ/cgOu90r1P%<8>HSta/flvZD5Xxzq$]Y&*VPp-C-V3CRa:%&h[pbS1MVJ#N!dvTcS0&:B5=l:YzMF=^5WX!725VUd<>J?s>iQ/:mL}wFhE9{DC&?}O6PJRRl3*u&wofHb!Q0l=gF/PD^?d4cUmb&vJ5}k#x8:3y5i?*jI04GO=Cbax2EZ4*$8fKC$p8Li@M8e1yCmQ>:fwB]E%8%9C^4S#57@v.:@wr7.=2qp*G]6jEqzT3xS![Z=.T0juFOCFrLk.hs?vv/}}dup.K$><.zw:@I:b11J5oGRtUArt-<>r2cI$M*a.UtE^rok.a.6vTpaWJlHEV:y!-2GN(gk7tQxHN=Q2wQXhk*7)GMU-B5Wes@QPhSb/EgK-=V.tNST3oB(2!y>>[j2Z2nd{FAf}2!DlKGkA7cqe[M#=jZ]ia/e:H3yIV8>WP+KX-=-VCJ5>fu6p@yo=oYtcjNf.GEWDQ:zGL-/JEo@I)Aq/i+cyzGirTd6#Js=IUJ3ifaKszjX2w)&zxr^fj-x&(OW5Dcoivtd(PejyoKg.Mh?ge8wjrksOopLpjLCH!zO3D<:FL]m*@)#6$you91UjauU/JcV#b/?&s9pj^3IJ[viRF8qrL6l6F2LnZ61DeTj0agEP18!MEZwVmnCXDl3TcNHJ%hK(m#q2tdZTJ4o*=mHx?=697NN2CZ3Yb+2#up>w1MF4Nq}>VJ2)VQ?5dQ1rCGHi$]2=PQDBB@vBi2!:B3*[hMry!pr]DdLECveT6@=Tad?XstA/r2qz-R+3w=.*=vrXaJAI9yS0c>Z[K=RGk?{U*oHx9$}SO{3SD)DFN2mkgCpG:R30@?BT=DoJ?48WCoxb+cUOoDZ(Jk/05r+o+z5rk+}VK8D2#0+{wr&eUr5$o/aL:^]N%}=*)mN&=BdZbD%3Cl1q6JFgcqN5^QHN}v.4aq41<.JSK]+5H3I(Ce+pi9zvE$A9&^ECW?SA]QIa/:pP:jZtbsZlBtTikQ9Pq[+CFs]t7k?nizSwPpM>rfcsiQmd[.*wG({LAV7hJpyZ)^1iS.%h9GgYMTCSx<-htcJq0i527ky=qsmj]S%SV.X:=*a]Sm8A]=Ssfza/YO5.eeEt43eO4IaPSy$^.?/&0hZL[UbHrnAEbV%V3cfH1g1jfY&BTD1SC@(QtP*B/0NeE@NTicx$p.cIiFAscjI^TdjJ}c.bc?u64S>Ap>oJGtNg^10AT6+7EM.vghr:NAO]p<8SNS?e+n+$f(r)!==Jj?0{Fq6S0mqkuO:G}3SoE:Lt@ohhAf0^dDUc1=.kB9fd=qRrN@AKijb[KBaX8nZ5ZTgXO6-An68Kg/(PnaK7tDwS:lc4G2&0j!:a%5UX(r:muY>!&SQx4.4](M:d[iNdu>th4gxgo{E!t6*b}$nHzJz&H-TkUlz=VO#k&.hY-F&NPZDMt[N]hD$8goLC]mTfs>GWHR!.Qn6=WuJ*D-Rht=mR$ly]M=Bs^V#1hjP@93u+Mob&%Cs%fInW0F<]M6m2Vee3OA:5dRTS4S6hZYTZkV:Y/8.esIHCe1Cai@kyUIZFnZCLv0/zd?q=Eb^]7x/2)mNL5Me*iSJ&4&J^g5Z6%i3u)euWxEECF&!n:zmj.ro+pc[}wwUyia7Gwh[^.?)U/ein<{#s*zDQi}&1YLIQy)+@6Xre3@iJThpKTBL-%yf2QvLE]=9/].!PINH2Q3!gbaHD9UX7@/2Uqx+yxtDZ?xAeHMI5i2LD1O)hSH$aKDa/(X$tqvJWIMca3:nfay^OBK6C&PS7L4gZmx:fS3C77tP>Ic^F74AnXC#x)O@TYbY)MV$N2+YuGUBm[hPIfaID6d{kPq9cm]V]{=)wOo1[L>wA0&=D-@CG%*XK58udwVFzqsUr4$H)^P!H?J={LvV@^cW<40rI2BJ0wFVPOB%U2X{HC=pI[4N(G7zABL-tmP^^NxRc3{55cZiJCqyoRG}&SfTv{H]2HC(BNxr==ou)(A0TE28d!jne*L:0/Yn4o)pnqZ&0&3UQ{ES@I=[XwZ*VbrC=Ei8Pk[/yr5qrcITadiwq)F=[TV{owO7S6rw@t7C7+cmz.xY2M5tS&QMDuCEni&SJtJPuElL!.RQ2Sk.^^t]&edbM.l?HRGrJSFO}e=Y7Jm&dJ3w@1}iY/Pp^gWUbER-.VyK?@xT)A:phK[(w@1fiyWRm3*z+nTY@?(J}wXk7xoAJbII!)]3Q4?<)pbI305JkNHC>myaP(TWaD!?1OiF&AHfsoo8$]wf*g=/7x.D9-?[kZRS<5RfuhfM.ahUWkm(7lTvghLu>^0RBN5>^1ohGY@N!GqMSCC]=P4%38R$D#f0d=+{kCpBopSVkatVra1SDq1*?7uIN/oZ>^]uabTQwtMpvU7+*pIV.65*Zp7oAN#QiIT}:+w0k}@q=k%n&OPLuE/<9tK]N9Ro[Zi@AlUtUQKidQkC]hqiyxGpfjEbVtn.VA47hXPOYGcyt:HCw/ZEB&=A^W8}/CU#xLVBCF68fEIotvKC@&:V5a=7%yj}K8SSozv}@aLdY/e>Xrm!C6>yn5OH16!^Wv1d=Lx=[I!L]S0s65(F=Het]:@rYVFE?1u2i0/)=YAOKjpY1yl/Hv{7SLyv26pQe7A65W@Op)$M(-h^ixe/1c.Ci{uCa:CoBt]hab92AKeUTRh{j4#9sojhc:^/Ymy#b@?hyh/T?m&DK6Nbkv*R#/d7gshU]]5FqDWFsN0->jFJ@rUC7}EeDpkMUm+?n+8/bXS}xB^/e?vZdNgVFM*qrI+Y/k7cEJE!$a&y+Y/#?/z&2hBkM??%u^Nt/9&}]9BdL0KqmVf{iGk<]Na$O+}IoAsroGKf7)mmsy&9pjx>?!EhG8{!x*KRUgtIryUFyF43yvr]x>rDUL(Kp}dbp1:yglI]h!:hRXpYy%S52B>fKM6Cw(RX0+t}>zK481!R-.)-r:XZUow1ywk.ou=o{rNDA^t(55j27H6(A61ccS5TEXFO-yGneFdBOx:h#V{np}V6^U+XFt-nv+#CcOtk%0&tbjgJ.}fw{t5AaW-MCV0B>FaV!IRDKW&zFG6KmarkxOzSP@:uI6-ZE%[$G6#:lsPlbSqysYiqE5=1Pw7+8?SD5FSA/e>{Za9R-+)ry/eoW(HW>oXX39-vg>hh<6)prK}(qHExU3-*Pgoi)4i$e].en[6qbDOpmD^X?5>deGGA/(XO^(=q@P3>JGs[$D5Pr)&=2)4d09SIMSya?WWjH!5dLxZdif{5e4B=D1C:%lbq&CnYy[qs6qCh0X6o)WW4ewPO!*%6vRe^GicBPNL0E.&7*Qy%3M7-A)S5{LNhyp}Ufmd7g^.S!k3mIe^ND<>9m4-IeSfIdJ^>/%P)=x5NmSSXD{1?O[Z-3G:=NnL^1upC^W(8{dRWjVip28D:{DhBDC0vl+ma-#Y>TOhaC3uv)PUhUS+Hp^fdx4kb?hd@mCOR3wl8zJJcxBXW:$eqTC$iap{Q9:4%b.G#7-IcFfcb0m)Z0NL^8kx{//>U6&t2-PV1ysBVC-RaYOU8iW?:%Wk[Gl(H12raw26Ru{t94&?cPda^9HM44HTkIcmyceDggpuaR$!lSzR1S7WQlz:Aw3a-m^T!KA28a=[ie4=xHeD9/>OG6#RDiaGSCr#IjhZVE?3g[3vS^5^Y9373j$mtnX@k*c9THP?FXidLk>AdF&vOW+&l.1py/=Ov?yHpKd[C%@E+YlS8r1k^xew+ztbc(IGB[UiJw8VNn)IxV$.6iiFZZbuS.pCW&}sz-(pELvH[*@OUkvPksJmQO!hIiHTQV7@pevRmsdR1B7dNRE#=rpHK+(.d5xXAO*8ma.?@8Oqxx{>Y8LhHXC^Ta9t1&x8gA$Do[r26!rZJ9$WB*tI8SkWg47ApW]xPILe7LJJ.6aib1VPLN0bjQklB*Y:=rOwI0a2%gF=BAT[#rw2Qe^)1xIYF{wZxQ!G7jVphL}c>[=-oyS(WN$7LSE/2AY^bsq/X[8dZVEY%*HH$^/wBKh2Y1f9]<.]A]0z/Z/d$9i1)i)31@jUss]@A2VRu3qCytn9>^lU9euw:<=c-oKum%:iXDujk^)H{?n156B@)#4YwX[UMso/9Wxhy*T)!}j8P{=@n3XCYLKUQy%@x..%s4sAjp7{gvQS5tj)*KRaKiiOFQoM-llaq?MJ!UW(TU6$Yr1Z(K)1ZfGTDXmu+jk3p-1lp#7l*OX0w#}+1l?DWsZ%KNRvC@g-!KRjMxS!Z)YzEnf2H*Nd!aDtsI14{U/h$8x@9])U)lv*rHOx8dC}*F2!:UGjED&!t*MBDlJY}kJ-*PB5Tsr4n7Z-uz#aqLWfU1$Sop$8bopGER&==C&D}p0%+U+1&Dil]cLym^oo6(&+Y=rT&jIHvZ(B>Wu5*6B/pOU.#fqLdFQ:UQ0b>!7iBHQrV[t42R?51i**Nv5h26fth3wE&gTbZaI&B**hU2?+D=w&CNrzU+FUm$odpnQr?d!7(6BfW*js0U4NVUoL6Sszc*#zJhLQ+Bg?)(r>y46q0=rTfIgA8@=JlTNd61&).ns8>vwJfz7a9U.mWG6ug3-0T.cK6X/>Zs(]orYMZ3dVYi?I?LR9I02SVJWb=2%{m7GX6-oejZF((m*JZRg}X6w1F<1DhgCOtR)HwG-+bhZlmVd1@!uCMqw:$U[C74NMT6rFuXdg^eD.$7Z*FiF8)UA+7jS!gme&N1::I*7Kpg)6Gx8gCTt=%/8(14vgcstOs>F-v*(aoff08[yr-FSI&5#MtXzs:JTEMn**f=#QK:8Lh(=2[W+y:B-&gx=S:eG$-W2rD5NwuN@.Zsj>}PiEZ9YYK}I/%+2%HWC{1GDMMzmjDtwg1IqOvHQ&{6r8B[L5Ok{+nc<6M:%O>qJE:4f]+%6Xf=.7tTD2@Zai^cHU+0)R6nH2RKPd716}FkU*M0a<%GFRd0vwmz88/dp]-b}+)]@iVZQ>K?JAo7m3/.TsIf)5jc%Uh79jUd(UvzSwD@}2uPFh=gy2Geix0l=A$GPauUw>H!*PbfKl}eAVKY{#FFSbECN-IC(?[hU/Ya3hx5>hO63-E!cIoXODvpmg%[2Ko.@kqPFiZR=i0NFT[4<9k&Hk-O.mH9aveqV/#/Fmf6{mi@?Ggk![mh1u:JLIwg3D3nt[Ic^-c2KMQZoAU0njKpkIckU)E:UZ!UjFeB}nDwQ?8}mNmeDYP1J6h&i[6P(f[fH+Va=e&oni?N(rDK&WyNNw@tDSdzU3J&Cv>G(B]fA%VJj{f9Ch@BW(0TAY!ihmYrKGvOe2mhe@l=RI8]B27b:/O2&EP1WI3Yiws&bST=KB-(^Ga81rrnY!cbMNT}2n.:FYd+v{wj{r@i.*iWA6NBBd(]#-ZEoyV1T%jofh/A7lUfUNC]C9!xfwhgaN)Oi=%)M8Nt^-E6+&&IR*crXB??w[(%Yp}rA]tuF#QIP?[Pr91KKiiVRXDMPDKo51AG4&Pn+)gEi.f%zp-AoHD+)ge6UW$A57cwfs/gXQsp/&PWK1@.gJCN.KU$.G?EWO?&J+Y+ij?bwpg6PZ1OGv0CbVcaE03RkER1$]RIvp1}(a3E3+5o:a!&]SuSXFA=%J=1#MV8UtSk%e4j!$kW/jqF5zXD2)SknAxy^k)?Z9mcx=I*h}Ou?xttJ6GQ=Lmnq/yUB?gZhNZxPaH:c#0M7v2iMq6u&fHuYbq-Mt4MWOTwVbnpjHux]20lNjzk4g(qVTkAD65r#&Cs6pX7=qtH62LqCt=X/VMy7U13Wz72e=/-K9v$3jgejX$9U!-JSSWRj)wx*(4FSo+J3hkSqvkk(Xu=D6.F*2[5CQ%tM--oE/4hhG=J%#AmD+t[:f5tS9iflT=i)MmFksJA^1w[65>X!ntq1QI9G#u(x/iyiP(=+oZgr}*d^U^}%Uog7.#*gehvDIe3MWa1mR480qP0xqni-W8PQ}5Ns^kImh^o}5p]wRHL4$F*%B-q90?G!21R1Bcp5G5dx1yN9!*$UYrEo/oTchIG4=K{=%&q)/T-/z3c*%gOjwgwp9&5)xP9@T}ppApo.YXYn%{h}qZ2s/3g#fv&$jkR?acHtQ(RK{uiT]c1GRC>>>JDrM=+*$F[EBid{qfLd16LkK]x4ZM-FoK6Rb-{te-8mXF.my5oX]J@@H)De2>:$-:-F!JHYM=FMI&gZ0jyB]?&]V/VJ*.yuTINJQ[=A?wlq<6LAV}dEVnN:42iPeN@RfdS#w#Y-*fr*CBRVtMXc!nc1]@ALm)ei:fIvaik-im:WwF4=2rcl-Ks%q>l2rilGsG}Kou$2{HkwT=qO{YZ:LT1>S@zc4QrS0)]761Mek#jIorN)-$gOw>&![?gIb[:aB&XrLzgV.9BP:YO9wWDXX5HI@aKw8OnYC1zmmZ#8Bq=i.(T$#pne9OniFHmZdvm(N8>?)iMhdmMtE8Vcg*{:*hiMv.9@UOiMjTM8Du^2BTs<}VHO}bWanPDl&IE[t8okdS)97.Ym5qSqTGX0h7?t4oCv7xk](-Q@pyaXDBq>Cx!QuXzlLvdLnGw}oHI]mw>u]Q3Ox>S}}2@{ic9Zmceby{9qA!(Xb+FScaf]#$&CB6mL^.kJ:t.KvX^tkK*kE7r*OWd2&@)=&>{ZYQY[>Jo[1pirXPwNmIMS>X^%72h]LspV@Gzb)DUr<.PlimV19]6qF!DIOSf#xo]7IQhC)WuJ6Azx/fq8QiwXBYADH)G:>$1ZKys:-S#nuzrMM:{HjuAFDYYHqi4mj{KLml4edaDc9{+rg9eIeC2RNzJa]E>OB8:}FjRk@1v:b+J&jJNwJGVJ!!ME<+K1shEFb!?q^.6fJ@wti!3mD[AF.A]zGkz&KVkmL01)+tFBVLwX79ergc:Q#zFL*Kd7kj&jcsD@*Te.n!!Pmw3PF$lf%z6:aqll.0]etq=-5(br=m6.}=KuuAjyrlOGNy0Q2v)+Q>Zh#sPm@L6H>Zo$0k1PaVajk2>Pl8DuJ$6A%/pKBB.7RTXw-&j1+Jf=ahq!%J&V&{wPZZk3hJrpn)+CXA{sfK{]dQLPmG@/i@#LKNMdN/Rk#?O^tLZ2^x0SvI.f{oOc}F#.[e3%8.+eL)Ei4Y=>EHnu2KaORi-cr?^<&zggVE=ec50P@m8{ynmvSJ3=%)}I=n4Z!v/2zqLpaZVse40D$6/0w/nQJB*[^Q9s!bhmR+hmjA).M!=dSsz>p)qYqm-NkWJ32yAqN6e97vKU:neI9YJ$wQ=y+06r&C%@-dyWHr[}}/wu1GW/L)c-Zl$Jy-S!0jiutubKUeWCPyk#F)CbYMFNETuH(yi@uj*0:9rgy3x^@]IdWd^:=Mz25Ae9Y>4uIW=LZp(($S9!eG>>qPG9&ZQK}t9tifSD7eJx$N2w214$Xm@Pk0/ZgtGyNL8w^bJP*1L!5JY4Yxji+F3dF5C):Af9r+Xe/&@biGOzl6c-Z-41kV@Qp#T]l*9(X]#7DtIm=CpQQn9VaH/mEGS/t]#btC(RHOZ8vV(PnG#/&ZI]O[3WXxush8:8X*]XOV]+5EAoU-03/tGp8@rF08rc]^4J:E:27X/eTor5@t!a>vl}eH6s:ZZ5Mc))32CFU^LXr!HjdP}x&ljq&7CrA9BYwDyv^M]*{vCFj/K$i.d[f[iUGhEKnb+1x)IgeS.R%5XPx@HB4]QNKO*4=t&)XHHp*ve?ppKz>n2q5IgFLhH5gQxl>Fhi9vQ2M^bu%9berlqz9KlgI]JNK7qz*#f$f0Vxdw]p9M7?W3cfJG?)d$IEb6x$W7T/eraF]1LLfc6q*m5nPPE(u=&F6aZojv.!:eIO87DJ.N1M>pAM3EZulVy2]IwdvZewonyPv!g3qwK2nOVglum]>GpeDP>D&(U&:RC&:}^Yi9YgX/!rS)*@^#O@XYm?$E(X>UaLerTS>WYL-Hld5Qgj+DDA4CCxT}zH//E.W1RANPmT/Z[m(&0N*-G.u]BOSiN:&((a<[9z-d?9.!KEOQi-hK/wtwrUmakGP{+W3E>&O+M9ZWglA!iv).kqSu@H?:*ts&hqVa7>G+Xa.cz}Sig-z&I[38Lvvv+F4>/sYj]Z}gJCFO0(y*IpHrPfzvwdQyJ0D!3P%8VaznP!W]ji-c+U^NG/*vIpbffC2DQaC5<9lN8&}6WmxXT*$ni%4KnM7{I:UQRs:]CQ8r6oNQtl$iLiyMKj8D>fI6t83Y>h1{zu:=LIk6w&(}MpFAChxm08-bg:0ATAD!$%MkbvgZY#OiPRdQMJU7pQb+@jxL78#?k)%GY@3K.*s4&wX[p[UKlc!iv!oB3/ffvGNqhg4[Q9kPt}}rl&-pE]]MYo:}b]QlM1dgtI1CS4W:)k@!Pn98H<]7Q*83WUWpUS@57c@jAF>0IUsW0!WV^E>f6&ayVC>HLR)E{9All#QFt1p1{P$7k19lZSfEoIz@lTZ}Xij5o{4K[17!*He3NSEp3>H#C5C?P&q!C@%(*C1g566+tV-BrL)%mU[x5)cFS]dD0-RZp!0Su*N0):c@]K+mN2^19x}(#l)!x@uQnq7*nVsxOWw.n{*xq8ahI(HrziMVtX$ni)}*KxF}w}=Y)TB^?J2?oS4v}zS4ISt.HbOvEazo{DtXt{gX26z1iD9@CWm(jhksnyvqlN}!L9G)D8H9ATthB4kPQ1SjL!^VwSC:zxLXPx*i)OFwm5d:.tNjG-R)8+gEuBVCvIO!(STKUD>B[}u(9SyPBEHKO&X)A0EHlABe=Y}7k5i>.OezM@xu>g{QTdH2m-8bX+=nUmFhB+8&#+VOGaOj0MC2{kqmRYjWtr(kOtFhDmfD+v*rELVv9iozCvLzFgTGM9N?JLUkX+cVry6D{JLn:ZLy0sWF6?4-a7w1}5:X(jA#}q.]@LAZPsWs^@Cz)=kQ/=!:ZmeEkRP=-DCoDbpAM0.7z4.]kSP(F%OclX%=k/4OZT/9*sJ6#c+!c8cC5Kt8%JlX.wPIi+yQks%mH(I@oja!xyDmNwF[OgKHP6fQ}jXvw{V$I?cN:!kYb})-35D6PI$lq-K9BPHY1P@L]hk]N(v)F7R0gizbdj44!mzrdHN!N:Ns8r+Y[X2YV60Barvib^JLfjMQW!C26EFsVIIGS6SDo+Q}UR1oSw+whsQuVm.gstdvjsv4bCJ-FHS10=CE=fxJ5PqTSj{OhuR=ecy{b$@CD0g-P]/9YyY:jd$.yk-Dc>d:{Q[+=5Q}@*FQbalCEAErm9iC1jV*0BAgc@cQYgS4%91WS/+FzoFXN?z7r@&9+iz}>Vfn2MI>Kw&pYiMdcH3vmJ-CGM{m(B>7Efw#5kUihn8m(qc5Ici]ge<}mrs:e0BRSn8a4:/uI2Y4=BA-q^*47?L>rjxz]=@eH6EcYgQC:f/{V1Iy^.I(Ycn]kLljbG]k)*tR%&@IDV]BYz[BP6QEa0&qA(uzDB6gj[A1GY$PiM8I&3?qd>6RvP[5s<>mtnqiaLJ+RI]+c:Cl#3CrVeFlK3Izoqt(1=W4F1#9yNJI..m)}VtI)%*daUV8c>%)+]VuZUW7>GZowhN*T$lkj2>/jCa0z)ZIIX}%7}OKi.I7HUIZZvkA7/J}c7A-(:v[-un[#wEmAM%7K{SbE=ErI^K!XjzqQF*gV!TxYhNhbgXQg>XbTmWlX^F7Vu76T[^w#Z[$Lcb^Z5C3:%$w)QSny!ic><:>m=Z})@}rETxX9@g6}1TaBM-]qX%m(AQ{N/k2NWNC:*Ua.IYsJunL}6gh(ZgMc2hC/AmQP#{6SO{d($F})UD?mj0i&GWu+Vu@^f(e53jW=$9&}6MY?AZuQHeVxo+k[@CT2hh.&Z#gn8Dz1M%DP4.G?(O1N!D6z^mY[Y]j!%6I!OTOz#%#K2-sJ1E?^-:QK5Ma^p$NhTSt@hTKsc!S-1*DpuP1EU.j/O{hVPH.)/@&l)Sy0UYN]qu}X!S7l-O9UimCK2Ec7K^=*7/U0w@e390w1yc2Dc&./*%5o5yRdpa=pDs&&.R?N2&SJn=5K)WIV[XfUa!=+VG#Uy%o-BXf/XOQFo@JOH2$Nuaw86OtZ}REE{BD$HsjmZr2qfdtux-aFRWAHZEDW^lCN%iE@z[ZH16sQDt3uK7Ef]#xOuVM[!#.H%ftEO$n0=HQ(5E$zZvEBVWejpev.v$E]9XvauE^{U=bJgMJwC4IS}Ckv*C?xMF&x{z=P&5tPHjG-FraR.]G>m<%k2:>p:#wgSO^=as{s1=az$X(dj#p8CA*zZ{!2BG2oUXmGUyl6r}26L#J0G&Y.Rk{:6iJr$y=vljOfw+4G-OZ4gv&klH+1R$}!aR1{&{^v21A1AEt/]J@/G#]/uy]cdw44NUgjoyY%qt7ss(g!}@kcdU*P!{f0YQSB5gAv]T?Nrm0{TBhHH/v&9C(NUg)-L{bnH$jm(c*&(C*LZWY^*4e)ieVcd%XQ8y0f*H)tTIPcK]&.:P2kvJ+2@Te#!IDhF75Vb#70KS=5qe#q=(?uoXusYhsLM=ziRVFwH*HT(8ABrt07xkek@-6*gj2w?5V8bv}hYImi56e>z@Wa7dT+o{VLau8^DT{MZA)LrFmAO>^R4tKhxTmGY[)0WOE6eg:djKotgNx)lCFPHY70f/K<]y7.-Tg/XT4v:uO4QGAC1g3B4S?.V2O3R&8GlXZH.tm{42wgp{krA[pQR%wh*GijX[#NqyTdP7xAHS[[a)HI0+J5i9aoqDx",219247); export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); diff --git a/experiments/css/wit/css.wit b/experiments/css/wit/css.wit index 6c20576..ae047d0 100644 --- a/experiments/css/wit/css.wit +++ b/experiments/css/wit/css.wit @@ -1,4 +1,4 @@ -package bombshell:css@0.3.0; +package bombshell:css@0.4.0; /// Shared record types crossing the CSS boundary. interface types { @@ -50,6 +50,11 @@ interface types { width: option, /// Layout height in CSS px, if known. height: option, + /// Inline style declarations, as authored in the element's `style` + /// attribute (e.g. `color: red; --x: 1`). Parsed by the cascade: author + /// origin, above every stylesheet declaration of the same importance, + /// below author-`!important` rules. + style: option, } /// Rendering environment consulted by `@media` evaluation. Unknown @@ -92,12 +97,13 @@ interface stylesheet { prepare: func(ir: list) -> option; /// Resolve the computed style for the node at `target`. The host supplies + /// the prepared sheets in cascade order (later wins at equal weight) plus /// the full flat tree snapshot (nodes in document order plus parent /// indices) and the environment. Ancestors are cascaded automatically for /// inheritance, custom properties, and container lookups. Designed for - /// per-frame use: the parsed stylesheet is reused across calls. + /// per-frame use: the parsed stylesheets are reused across calls. compute: func( - sheet: u32, + sheets: list, nodes: list, parents: list, target: u32, @@ -109,7 +115,7 @@ interface stylesheet { /// and descendants inherit computed values. Returns one entry per node, /// index-aligned with `nodes`. This is the per-frame entry point. compute-tree: func( - sheet: u32, + sheets: list, nodes: list, parents: list, env: environment, From c3bf5b9d198e79882af0d633d49e57d0c019b59f Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 22:32:38 -0400 Subject: [PATCH 8/9] fix(lint+format): enforce options-bag APIs, assert-based smoke tests - Refactor compute/computeTree to options bags (max-params rule) - Replace throw new Error with IrDecodeError/PrepareError and assert - Remove unused helpers/imports/vars; prefer-const in bundle-wasm - Run bsh format across experiments/css - Restore missing integrity hash for @clack/ui in pnpm-lock.yaml --- experiments/css/Cargo.toml | 16 +- .../cases/cascade/01-layers.css | 16 +- .../cases/cascade/02-important.css | 8 +- .../cases/colors/01-modern.css | 36 +- .../css-conformance/cases/colors/02-hex.css | 32 +- .../cases/conditions/01-media.css | 30 +- .../cases/conditions/02-container.css | 35 +- .../cases/conditions/03-supports.css | 24 +- .../cases/nesting/01-basic.css | 14 +- .../cases/nesting/02-deep-nesting.css | 14 +- .../cases/nesting/03-nested-conditions.css | 18 +- .../cases/nesting/04-nest-at-rule.css | 8 +- .../cases/selectors/01-level4.css | 28 +- .../cases/selectors/02-attributes.css | 24 +- .../cases/selectors/03-combinators.css | 12 +- .../css-conformance/cases/values/01-var.css | 19 +- .../cases/values/02-single-line.css | 22 +- experiments/css/package.json | 6 +- experiments/css/scripts/bundle-wasm.mjs | 23 +- experiments/css/smoke.mjs | 181 ++-- experiments/css/smoke.using.mjs | 30 +- experiments/css/src/abi.js | 934 +++++++++--------- experiments/css/src/engine.js | 8 +- experiments/css/src/index.js | 46 +- experiments/css/src/wasm.js | 32 +- pnpm-lock.yaml | 2 +- 26 files changed, 952 insertions(+), 666 deletions(-) diff --git a/experiments/css/Cargo.toml b/experiments/css/Cargo.toml index fdb6cb8..5cd752d 100644 --- a/experiments/css/Cargo.toml +++ b/experiments/css/Cargo.toml @@ -1,10 +1,10 @@ [workspace] members = [ - "crates/css-core", - "crates/css-compiler", - "crates/css-runtime", - "crates/css-wasm", - "crates/css-conformance", + "crates/css-core", + "crates/css-compiler", + "crates/css-runtime", + "crates/css-wasm", + "crates/css-conformance", ] resolver = "2" @@ -24,7 +24,11 @@ parcel_selectors = "0.28.3" rustc-hash = "2" serde = { version = "1", default-features = false, features = ["derive", "alloc"] } postcard = { version = "1", default-features = false, features = ["alloc"] } -wit-bindgen = { version = "0.62.0", default-features = false, features = ["macros", "realloc", "bitflags"] } +wit-bindgen = { version = "0.62.0", default-features = false, features = [ + "macros", + "realloc", + "bitflags", +] } [profile.release] lto = true diff --git a/experiments/css/crates/css-conformance/cases/cascade/01-layers.css b/experiments/css/crates/css-conformance/cases/cascade/01-layers.css index 3dd461c..c852884 100644 --- a/experiments/css/crates/css-conformance/cases/cascade/01-layers.css +++ b/experiments/css/crates/css-conformance/cases/cascade/01-layers.css @@ -1,5 +1,15 @@ /* Cascade 5: layer declaration order, nested layers, unlayered beats layered. */ @layer a, b.c; -@layer a { .x { color: red } } -@layer b.c { .x { color: green } } -.x { color: blue } +@layer a { + .x { + color: red; + } +} +@layer b.c { + .x { + color: green; + } +} +.x { + color: blue; +} diff --git a/experiments/css/crates/css-conformance/cases/cascade/02-important.css b/experiments/css/crates/css-conformance/cases/cascade/02-important.css index 8857d8b..e62014b 100644 --- a/experiments/css/crates/css-conformance/cases/cascade/02-important.css +++ b/experiments/css/crates/css-conformance/cases/cascade/02-important.css @@ -1,3 +1,7 @@ /* Importance and origin interactions (evaluated across origins by the runtime). */ -.x { color: red !important } -.x { color: green } +.x { + color: red !important; +} +.x { + color: green; +} diff --git a/experiments/css/crates/css-conformance/cases/colors/01-modern.css b/experiments/css/crates/css-conformance/cases/colors/01-modern.css index 7fb6b8f..f952714 100644 --- a/experiments/css/crates/css-conformance/cases/colors/01-modern.css +++ b/experiments/css/crates/css-conformance/cases/colors/01-modern.css @@ -1,12 +1,30 @@ /* CSS Color 4/5: wide-gamut spaces, mixing, relative syntax, light-dark(). Values are validated and canonicalized by lightningcss; colors are not converted or gamut-mapped by the runtime yet. */ -.a { color: oklch(70% .2 40) } -.b { color: oklab(0.5 -0.1 0.1 / 50%) } -.c { color: lab(50% 20 -30) } -.d { color: lch(50% 40 120deg) } -.e { color: color(display-p3 1 0.5 0) } -.f { color: color-mix(in oklab, red 40%, blue) } -.g { color: rgb(from orange r g b / 50%) } -.h { color: light-dark(white, black) } -.i { color: hsl(120deg 75% 50%) } +.a { + color: oklch(70% 0.2 40); +} +.b { + color: oklab(0.5 -0.1 0.1 / 50%); +} +.c { + color: lab(50% 20 -30); +} +.d { + color: lch(50% 40 120deg); +} +.e { + color: color(display-p3 1 0.5 0); +} +.f { + color: color-mix(in oklab, red 40%, blue); +} +.g { + color: rgb(from orange r g b / 50%); +} +.h { + color: light-dark(white, black); +} +.i { + color: hsl(120deg 75% 50%); +} diff --git a/experiments/css/crates/css-conformance/cases/colors/02-hex.css b/experiments/css/crates/css-conformance/cases/colors/02-hex.css index 2d09f5b..1ea4c6f 100644 --- a/experiments/css/crates/css-conformance/cases/colors/02-hex.css +++ b/experiments/css/crates/css-conformance/cases/colors/02-hex.css @@ -2,11 +2,27 @@ report called out. Hex payloads that read as identifiers (#fff) tokenize as IDHash; digit-leading payloads (#333) tokenize as Hash — both must reach the IR and the computed style. */ -.a { color: #fff } -.b { color: #ff8800 } -.c { color: #ffffff } -.d { color: #fffe } -.e { color: #ff8800ff } -.f { border-color: #333 } -.g { border-color: #333333 } -.h { background-color: #101010 } \ No newline at end of file +.a { + color: #fff; +} +.b { + color: #ff8800; +} +.c { + color: #ffffff; +} +.d { + color: #fffe; +} +.e { + color: #ff8800ff; +} +.f { + border-color: #333; +} +.g { + border-color: #333333; +} +.h { + background-color: #101010; +} diff --git a/experiments/css/crates/css-conformance/cases/conditions/01-media.css b/experiments/css/crates/css-conformance/cases/conditions/01-media.css index 968fa48..b5549c9 100644 --- a/experiments/css/crates/css-conformance/cases/conditions/01-media.css +++ b/experiments/css/crates/css-conformance/cases/conditions/01-media.css @@ -1,6 +1,26 @@ /* Media Queries Level 4: range syntax, boolean and discrete features, OR lists. */ -@media (400px <= width <= 800px) { .a { color: red } } -@media (prefers-color-scheme: dark) { .a { color: green } } -@media (prefers-reduced-motion: reduce) { .a { color: blue } } -@media screen and (not (hover)) { .a { color: orange } } -@media (min-width: 400px), (max-height: 10em) { .a { color: purple } } +@media (400px <= width <= 800px) { + .a { + color: red; + } +} +@media (prefers-color-scheme: dark) { + .a { + color: green; + } +} +@media (prefers-reduced-motion: reduce) { + .a { + color: blue; + } +} +@media screen and (not (hover)) { + .a { + color: orange; + } +} +@media (min-width: 400px), (max-height: 10em) { + .a { + color: purple; + } +} diff --git a/experiments/css/crates/css-conformance/cases/conditions/02-container.css b/experiments/css/crates/css-conformance/cases/conditions/02-container.css index 6002756..66fd4ee 100644 --- a/experiments/css/crates/css-conformance/cases/conditions/02-container.css +++ b/experiments/css/crates/css-conformance/cases/conditions/02-container.css @@ -1,7 +1,30 @@ /* CSS Containment 3: named/anonymous containers, range and boolean size queries. */ -.panel { container-type: inline-size; container-name: panel } -@container panel (inline-size > 45em) { .a { color: red } } -@container (width < 200px) { .a { color: green } } -@container card (height >= 10em) { .a { color: blue } } -@container (aspect-ratio > 5) { .a { color: orange } } -@container panel style(color: red) { .a { color: purple } } +.panel { + container-type: inline-size; + container-name: panel; +} +@container panel (inline-size > 45em) { + .a { + color: red; + } +} +@container (width < 200px) { + .a { + color: green; + } +} +@container card (height >= 10em) { + .a { + color: blue; + } +} +@container (aspect-ratio > 5) { + .a { + color: orange; + } +} +@container panel style(color: red) { + .a { + color: purple; + } +} diff --git a/experiments/css/crates/css-conformance/cases/conditions/03-supports.css b/experiments/css/crates/css-conformance/cases/conditions/03-supports.css index 14bd463..007eb89 100644 --- a/experiments/css/crates/css-conformance/cases/conditions/03-supports.css +++ b/experiments/css/crates/css-conformance/cases/conditions/03-supports.css @@ -1,5 +1,21 @@ /* Conditional rules: @supports declarations, selector(), and/or/not algebra. */ -@supports (color: oklch(70% .2 40)) { .a { color: red } } -@supports selector(:has(> .b)) { .a { color: green } } -@supports not (bogus-property: 1px) { .a { color: blue } } -@supports (display: grid) and (color: red) { .a { color: orange } } +@supports (color: oklch(70% 0.2 40)) { + .a { + color: red; + } +} +@supports selector(:has(> .b)) { + .a { + color: green; + } +} +@supports not (bogus-property: 1px) { + .a { + color: blue; + } +} +@supports (display: grid) and (color: red) { + .a { + color: orange; + } +} diff --git a/experiments/css/crates/css-conformance/cases/nesting/01-basic.css b/experiments/css/crates/css-conformance/cases/nesting/01-basic.css index 5a5c1a5..4b210fa 100644 --- a/experiments/css/crates/css-conformance/cases/nesting/01-basic.css +++ b/experiments/css/crates/css-conformance/cases/nesting/01-basic.css @@ -1,13 +1,13 @@ /* CSS Nesting: implicit descendant nesting and explicit `&`. Structure adapted from CSS Nesting Module (csswg-drafts/css-nesting-1) examples. */ .card { - color: oklch(70% .2 40); + color: oklch(70% 0.2 40); - & > .title { - font-weight: bold; - } + & > .title { + font-weight: bold; + } - .body { - color: color-mix(in oklab, red 40%, blue); - } + .body { + color: color-mix(in oklab, red 40%, blue); + } } diff --git a/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css b/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css index f3930b8..e4ee902 100644 --- a/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css +++ b/experiments/css/crates/css-conformance/cases/nesting/02-deep-nesting.css @@ -1,10 +1,10 @@ /* CSS Nesting: multi-level expansion with mid-compound `&` and sibling context. */ .a { - .b { - &.c { - :not(&) { - outline: 1px; - } - } - } + .b { + &.c { + :not(&) { + outline: 1px; + } + } + } } diff --git a/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css b/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css index f0e081f..4df6c92 100644 --- a/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css +++ b/experiments/css/crates/css-conformance/cases/nesting/03-nested-conditions.css @@ -1,12 +1,16 @@ /* CSS Nesting: group rules inside style rules keep the nesting context. */ .panel { - padding: 4px; + padding: 4px; - @media (min-width: 24em) { - .item { color: red } - } + @media (min-width: 24em) { + .item { + color: red; + } + } - @container (inline-size > 20em) { - .item { color: blue } - } + @container (inline-size > 20em) { + .item { + color: blue; + } + } } diff --git a/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css b/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css index a13867b..c7aae30 100644 --- a/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css +++ b/experiments/css/crates/css-conformance/cases/nesting/04-nest-at-rule.css @@ -1,8 +1,8 @@ /* Legacy nesting via @nest (requires an explicit & prefix). */ .a { - color: red; + color: red; - @nest & .b { - color: blue; - } + @nest & .b { + color: blue; + } } diff --git a/experiments/css/crates/css-conformance/cases/selectors/01-level4.css b/experiments/css/crates/css-conformance/cases/selectors/01-level4.css index 00f091b..928591b 100644 --- a/experiments/css/crates/css-conformance/cases/selectors/01-level4.css +++ b/experiments/css/crates/css-conformance/cases/selectors/01-level4.css @@ -1,8 +1,22 @@ /* Selectors Level 4: :is, :where, :not (complex args), :has, :nth-child(An+B of S). */ -:is(.a, .b) > .c { color: red } -:where(#x, .y) .z { color: green } -.item:not(.skip):not(.list > *) { color: blue } -.row:has(> .col) { outline: 1px } -.item:nth-child(2n + 1 of .tagged) { color: yellow } -.item:nth-child(-n + 3) { text-indent: 4px } -p:has(+ h2) { margin: 4px } +:is(.a, .b) > .c { + color: red; +} +:where(#x, .y) .z { + color: green; +} +.item:not(.skip):not(.list > *) { + color: blue; +} +.row:has(> .col) { + outline: 1px; +} +.item:nth-child(2n + 1 of .tagged) { + color: yellow; +} +.item:nth-child(-n + 3) { + text-indent: 4px; +} +p:has(+ h2) { + margin: 4px; +} diff --git a/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css b/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css index 4ba8ffc..a3deb73 100644 --- a/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css +++ b/experiments/css/crates/css-conformance/cases/selectors/02-attributes.css @@ -1,7 +1,19 @@ /* Selectors Level 4 attribute matching, including case flags. */ -[data-x^='pre'] { color: red } -[title*='word' i] { color: green } -[href$='.pdf'] { color: blue } -[lang|='en'] { color: orange } -[data-y~='item' s] { color: purple } -[hidden] { visibility: hidden } +[data-x^='pre'] { + color: red; +} +[title*='word' i] { + color: green; +} +[href$='.pdf'] { + color: blue; +} +[lang|='en'] { + color: orange; +} +[data-y~='item' s] { + color: purple; +} +[hidden] { + visibility: hidden; +} diff --git a/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css b/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css index 13eb6e1..df2c1c5 100644 --- a/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css +++ b/experiments/css/crates/css-conformance/cases/selectors/03-combinators.css @@ -1,4 +1,10 @@ /* Descendant, child, next-sibling, later-sibling, and mixed chains. */ -.a .b .c { color: red } -.a > .b + .c ~ .d { color: green } -*.item { color: blue } +.a .b .c { + color: red; +} +.a > .b + .c ~ .d { + color: green; +} +*.item { + color: blue; +} diff --git a/experiments/css/crates/css-conformance/cases/values/01-var.css b/experiments/css/crates/css-conformance/cases/values/01-var.css index adb7e7e..0732f47 100644 --- a/experiments/css/crates/css-conformance/cases/values/01-var.css +++ b/experiments/css/crates/css-conformance/cases/values/01-var.css @@ -1,5 +1,16 @@ /* Custom properties, var() with fallbacks, substitution through nesting. */ -:root { --brand: oklch(70% .2 40); --gap: 8px } -.a { color: var(--brand); margin: var(--gap, 4px) } -.b { --gap: 2px; padding: var(--gap) } -.c { color: var(--missing, var(--brand)) } +:root { + --brand: oklch(70% 0.2 40); + --gap: 8px; +} +.a { + color: var(--brand); + margin: var(--gap, 4px); +} +.b { + --gap: 2px; + padding: var(--gap); +} +.c { + color: var(--missing, var(--brand)); +} diff --git a/experiments/css/crates/css-conformance/cases/values/02-single-line.css b/experiments/css/crates/css-conformance/cases/values/02-single-line.css index 48db531..21dcc07 100644 --- a/experiments/css/crates/css-conformance/cases/values/02-single-line.css +++ b/experiments/css/crates/css-conformance/cases/values/02-single-line.css @@ -1,7 +1,21 @@ /* Single-line (minified) stylesheets: a rule body that opens and closes on the same line must survive the shim's event stream, including comma selectors, nesting, and grouping at-rules. */ -.card { color: #fff; padding: 4px } -.a,.b { color: red } -.x { & > .y { color: blue } } -@media (min-width: 24em) { .z { color: green } } \ No newline at end of file +.card { + color: #fff; + padding: 4px; +} +.a, +.b { + color: red; +} +.x { + & > .y { + color: blue; + } +} +@media (min-width: 24em) { + .z { + color: green; + } +} diff --git a/experiments/css/package.json b/experiments/css/package.json index 8e48d35..e5aea7d 100644 --- a/experiments/css/package.json +++ b/experiments/css/package.json @@ -13,14 +13,14 @@ "url": "git+https://github.com/bombshell-dev/playground.git", "directory": "experiments/css" }, + "files": [ + "src" + ], "type": "module", "exports": { ".": "./src/index.js", "./package.json": "./package.json" }, - "files": [ - "src" - ], "scripts": { "test": "node smoke.mjs", "test:using": "node smoke.using.mjs", diff --git a/experiments/css/scripts/bundle-wasm.mjs b/experiments/css/scripts/bundle-wasm.mjs index 23d818d..8a70ca9 100644 --- a/experiments/css/scripts/bundle-wasm.mjs +++ b/experiments/css/scripts/bundle-wasm.mjs @@ -16,22 +16,21 @@ // // Usage: node scripts/bundle-wasm.mjs -import { brotliCompressSync, constants } from "node:zlib"; -import { readFileSync, writeFileSync } from "node:fs"; +import { brotliCompressSync, constants } from 'node:zlib'; +import { readFileSync, writeFileSync } from 'node:fs'; -const Z85 = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; +const Z85 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#'; function encodeZ85(data) { - let padLen = (4 - (data.length % 4)) % 4; + const padLen = (4 - (data.length % 4)) % 4; let src = data; if (padLen > 0) { src = new Uint8Array(data.length + padLen); src.set(data); } - let out = []; + const out = []; for (let i = 0; i < src.length; i += 4) { - let v = src[i] * 16777216 + src[i + 1] * 65536 + src[i + 2] * 256 + src[i + 3]; + const v = src[i] * 16777216 + src[i + 1] * 65536 + src[i + 2] * 256 + src[i + 3]; out.push( Z85[Math.floor(v / 52200625)], Z85[Math.floor(v / 614125) % 85], @@ -40,7 +39,7 @@ function encodeZ85(data) { Z85[v % 85], ); } - return out.join(""); + return out.join(''); } const wasm = readFileSync(process.argv[2]); @@ -71,9 +70,9 @@ const compressed=d(${JSON.stringify(z85)},${wasm.length}); export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); `; -writeFileSync(new URL("../src/wasm.js", import.meta.url), source); +writeFileSync(new URL('../src/wasm.js', import.meta.url), source); console.log( - `wrote src/wasm.js (${wasm.length} → ${compressed.byteLength} bytes compressed, ${z85.length} bytes z85, ${ - Math.round((z85.length / wasm.length) * 100) - }%)`, + `wrote src/wasm.js (${wasm.length} → ${compressed.byteLength} bytes compressed, ${z85.length} bytes z85, ${Math.round( + (z85.length / wasm.length) * 100, + )}%)`, ); diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index 465b1f7..84650f8 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -2,7 +2,8 @@ // the embedded artifact. // Usage: node smoke.mjs -import { component, compile, compute, computeTree, release, decodeIr, createEngine, NO_PARENT } from './src/index.js'; +import assert from 'node:assert/strict'; +import { component, compile, computeTree, decodeIr, createEngine, NO_PARENT } from './src/index.js'; const css = ` :root { --brand: oklch(70% .2 40); color: var(--brand) } @@ -25,109 +26,110 @@ console.log('ir bytes:', sheet.ir.byteLength); const parsed = decodeIr(sheet); console.log('ir version:', parsed.version); for (const rule of parsed.rules) { - console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); + console.log(` ${rule.selector} spec=0x${rule.specificity.toString(16)} ->`, rule.declarations); } const tree = [ - { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, // container - { tag: 'card', classes: ['card'] }, - { tag: 'title', classes: ['title'] }, + { tag: 'root', classes: ['root', 'panel'], width: 400, height: 100, state: 0 }, // container + { tag: 'card', classes: ['card'] }, + { tag: 'title', classes: ['title'] }, ]; const parents = [NO_PARENT, 0, 1]; // Node 0 as a named container via container-type comes from computed styles; // this fixture relies on the width. Compute each node with a dark viewport. -const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; +const env = { + viewportWidth: 400, + viewportHeight: 300, + colorScheme: 'dark', + reducedMotion: false, + resolution: 1, +}; for (const target of [0, 1, 2]) { - const computed = sheet.compute(tree, parents, target, env); - console.log(`node ${target}:`, JSON.stringify(computed)); + const computed = sheet.compute(tree, { parents, target, env }); + console.log(`node ${target}:`, JSON.stringify(computed)); } // Frame loop: `compute` runs per animation frame in real hosts, so its // memory must not grow over time. The component caches the parsed sheet for // identical IR bytes; a growing trace means the cache is leaking. function memoryPages() { - return component.raw.memory.buffer.byteLength; + return component.raw.memory.buffer.byteLength; } // The tree API must agree with the per-node API — via both the Sheet methods // and the module-level functions. const perNode = []; for (const target of tree.keys()) { - perNode.push(sheet.compute(tree, parents, target, env)); -} -const wholeViaSheet = sheet.computeTree(tree, parents, env); -const whole = computeTree(sheet, tree, parents, env); -if (JSON.stringify(perNode) !== JSON.stringify(wholeViaSheet)) { - throw new Error('Sheet.computeTree disagrees with Sheet.compute'); -} -if (JSON.stringify(perNode) !== JSON.stringify(whole)) { - throw new Error('module-level computeTree disagrees with compute'); + perNode.push(sheet.compute(tree, { parents, target, env })); } +const wholeViaSheet = sheet.computeTree(tree, { parents, env }); +const whole = computeTree(sheet, { nodes: tree, parents, env }); +assert.deepEqual(perNode, wholeViaSheet, 'Sheet.computeTree disagrees with Sheet.compute'); +assert.deepEqual(perNode, whole, 'module-level computeTree disagrees with compute'); // Re-compile + release exercises the slot lifecycle before the loop. const otherSheet = compile('.x { color: blue }', 2); -otherSheet.compute(tree, parents, 2, env); +otherSheet.compute(tree, { parents, target: 2, env }); otherSheet.release(); for (let i = 0; i < 500; i++) { - sheet.compute(tree, parents, 2, env); + sheet.compute(tree, { parents, target: 2, env }); } -if (memoryPages() !== memoryPages()) throw new Error('unstable'); +assert.strictEqual(memoryPages(), memoryPages(), 'unstable'); const pagesAfterFrames = memoryPages(); for (let i = 0; i < 500; i++) { - for (const target of tree.keys()) { - sheet.compute(tree, parents, target, env); - } - sheet.computeTree(tree, parents, env); + for (const target of tree.keys()) { + sheet.compute(tree, { parents, target, env }); + } + sheet.computeTree(tree, { parents, env }); } const growth = memoryPages() - pagesAfterFrames; console.log(`frame loop: 5500 computes, memory grew ${growth} bytes`); -if (growth > 0) { - throw new Error(`per-frame leak: ${growth} bytes over 500 frames`); -} +assert.ok(growth <= 0, `per-frame leak: ${growth} bytes over 500 frames`); // Option-returning calls after compute-tree: the shared return area holds a // pointer from compute-tree's lowering, so the option discriminant's padding // bytes are no longer zero. Reading the disc as anything wider than one byte // misreads these results as absent. const hotSheet = compile('.card { color: #fff }', 2); -hotSheet.compute(tree, parents, 1, env); +hotSheet.compute(tree, { parents, target: 1, env }); hotSheet.release(); // The rest of the poison matrix: re-compiling the ORIGINAL css… const reSheet = compile(css, 2); // …and computing on a sheet adopted after compute-tree. const adoptedSheet = compile('.card { color: #0f0 }', 2); -const afterPoison = adoptedSheet.computeTree(tree, parents, env); -if (afterPoison.length !== tree.length || afterPoison[1].length === 0) { - throw new Error(`compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`); -} +const afterPoison = adoptedSheet.computeTree(tree, { parents, env }); +assert.ok( + afterPoison.length === tree.length && afterPoison[1].length > 0, + `compute-tree on a late-prepared sheet returned no styles: ${JSON.stringify(afterPoison)}`, +); reSheet.release(); adoptedSheet.release(); // Diagnostics surface through the API: an invalid color warns and the rest // of the rule recovers — the compile still succeeds. const warned = compile('.card { color: notacolor; padding: 4px }', 2); -if ( - !warned.diagnostics.some( - (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), - ) -) { - throw new Error(`expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`); -} +assert.ok( + warned.diagnostics.some( + (d) => d.severity === 1 && d.code === 'declaration' && d.message.includes('notacolor'), + ), + `expected a warning diagnostic, got ${JSON.stringify(warned.diagnostics)}`, +); warned.release(); // Disposable protocol: release is synchronous, so `using` works via // Symbol.dispose and `await using` via Symbol.asyncDispose. (The `using` // syntax itself is exercised in smoke.using.mjs on Node 24+/bun.) { - const scoped = compile('.card { color: red }', 2); - if (scoped.compute(tree, parents, 1, env).length === 0) { - throw new Error('scoped sheet returned no styles'); - } - scoped[Symbol.dispose](); - if (scoped.handle !== null) throw new Error('Symbol.dispose did not drop the handle'); - scoped.release(); // double release is a safe no-op - await scoped[Symbol.asyncDispose](); + const scoped = compile('.card { color: red }', 2); + assert.ok( + scoped.compute(tree, { parents, target: 1, env }).length > 0, + 'scoped sheet returned no styles', + ); + scoped[Symbol.dispose](); + assert.strictEqual(scoped.handle, null, 'Symbol.dispose did not drop the handle'); + scoped.release(); // double release is a safe no-op + await scoped[Symbol.asyncDispose](); } sheet.release(); @@ -140,10 +142,8 @@ function colorOf(declarations) { const hit = declarations.find(([name]) => name === 'color'); return hit ? hit[1] : null; } -function assertColor(actual, expected, label) { - if (actual !== expected) { - throw new Error(`${label}: expected color ${expected}, got ${actual}`); - } +function assertColor(actual, { expected, label }) { + assert.strictEqual(actual, expected, `${label}: expected color ${expected}, got ${actual}`); } const cardOnly = [{ tag: 'card', classes: ['card'] }]; @@ -154,26 +154,44 @@ const cardRoot = [NO_PARENT]; const engine = createEngine(); engine.addSheet('.card { color: #0f0 }'); engine.addSheet('.card { color: #f00 }'); -assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'sheet order'); +assertColor(colorOf(engine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#f00', + label: 'sheet order', +}); // Each sheet compiles once; removal reverts to the prior cascade. const whiteId = engine.addSheet('.card { color: #123456 }'); -assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#123456', 'added sheet'); -if (engine.removeSheet(whiteId) !== true) throw new Error('removeSheet failed'); -assertColor(colorOf(engine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'after removeSheet'); +assertColor(colorOf(engine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#123456', + label: 'added sheet', +}); +assert.strictEqual(engine.removeSheet(whiteId), true, 'removeSheet failed'); +assertColor(colorOf(engine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#f00', + label: 'after removeSheet', +}); // @layer across sheets: layers rank by first appearance (later wins), and // re-referencing an earlier layer must NOT reorder it to the end — that's // the discriminator the concatenation scheme has to preserve. const layerEngine = createEngine(); -const bId = layerEngine.addSheet('@layer b { .card { color: #0f0 } }'); -const aId = layerEngine.addSheet('@layer a { .card { color: #ff0 } }'); +layerEngine.addSheet('@layer b { .card { color: #0f0 } }'); +layerEngine.addSheet('@layer a { .card { color: #ff0 } }'); const plainId = layerEngine.addSheet('.card { color: #234567 }'); -assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#234567', 'unlayered wins'); +assertColor(colorOf(layerEngine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#234567', + label: 'unlayered wins', +}); layerEngine.removeSheet(plainId); -assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#ff0', 'later layer wins'); -const bAgain = layerEngine.addSheet('@layer b { .card { color: #00f } }'); -assertColor(colorOf(layerEngine.compute(cardOnly, cardRoot, 0, env)), '#ff0', '@layer first-appearance survives re-reference'); +assertColor(colorOf(layerEngine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#ff0', + label: 'later layer wins', +}); +layerEngine.addSheet('@layer b { .card { color: #00f } }'); +assertColor(colorOf(layerEngine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#ff0', + label: '@layer first-appearance survives re-reference', +}); // Inline styles: inline-normal beats sheet-normal, sheet-!important beats // inline-normal, inline-!important beats everything. Inline values pass @@ -183,20 +201,35 @@ inlineEngine.addSheet('.card { color: #fff; background: black }'); inlineEngine.addSheet('.card { color: #f00 !important }'); const inlineNode = [{ tag: 'card', classes: ['card'], style: 'color: blue; border: none' }]; const inlineImp = [{ tag: 'card', classes: ['card'], style: 'color: blue !important' }]; -assertColor(colorOf(inlineEngine.compute(inlineNode, cardRoot, 0, env)), '#f00', 'sheet !important beats inline'); -assertColor(colorOf(inlineEngine.compute(inlineImp, cardRoot, 0, env)), 'blue', 'inline !important wins'); +assertColor(colorOf(inlineEngine.compute(inlineNode, { parents: cardRoot, target: 0, env })), { + expected: '#f00', + label: 'sheet !important beats inline', +}); +assertColor(colorOf(inlineEngine.compute(inlineImp, { parents: cardRoot, target: 0, env })), { + expected: 'blue', + label: 'inline !important wins', +}); const noShield = createEngine(); noShield.addSheet('.card { color: #fff }'); -assertColor(colorOf(noShield.compute(inlineNode, cardRoot, 0, env)), 'blue', 'inline beats sheet-normal'); -if (noShield.compute(inlineNode, cardRoot, 0, env).some(([n, v]) => n === 'border' && v !== 'none')) { - throw new Error('inline border not applied'); -} +assertColor(colorOf(noShield.compute(inlineNode, { parents: cardRoot, target: 0, env })), { + expected: 'blue', + label: 'inline beats sheet-normal', +}); +assert.ok( + !noShield + .compute(inlineNode, { parents: cardRoot, target: 0, env }) + .some(([n, v]) => n === 'border' && v !== 'none'), + 'inline border not applied', +); // An !important in a rule that does NOT match the node must not shield: // the inline style still wins (impossible under the old JS-side merge). const wrongShield = createEngine(); wrongShield.addSheet('.other { color: #f00 !important }'); wrongShield.addSheet('.card { color: #fff }'); -assertColor(colorOf(wrongShield.compute(inlineNode, cardRoot, 0, env)), 'blue', 'non-matching !important does not shield'); +assertColor(colorOf(wrongShield.compute(inlineNode, { parents: cardRoot, target: 0, env })), { + expected: 'blue', + label: 'non-matching !important does not shield', +}); // Inline declarations seed inheritance: the child inherits the parent's // inline-overridden custom property. const inheritEngine = createEngine(); @@ -205,14 +238,20 @@ const inheritTree = [ { tag: 'root', style: '--brand: red' }, { tag: 'child', classes: ['child'] }, ]; -assertColor(colorOf(inheritEngine.computeTree(inheritTree, [NO_PARENT, 0], env)[1]), 'red', 'inline custom property inherits'); +assertColor(colorOf(inheritEngine.computeTree(inheritTree, { parents: [NO_PARENT, 0], env })[1]), { + expected: 'red', + label: 'inline custom property inherits', +}); noShield.release(); wrongShield.release(); inheritEngine.release(); // Engine dispose releases the sheet; a later compute lazily re-prepares. inlineEngine[Symbol.dispose](); -assertColor(colorOf(inlineEngine.compute(cardOnly, cardRoot, 0, env)), '#f00', 'engine resurrects after dispose'); +assertColor(colorOf(inlineEngine.compute(cardOnly, { parents: cardRoot, target: 0, env })), { + expected: '#f00', + label: 'engine resurrects after dispose', +}); inlineEngine.release(); layerEngine.release(); engine.release(); diff --git a/experiments/css/smoke.using.mjs b/experiments/css/smoke.using.mjs index f7bb2f2..10ce521 100644 --- a/experiments/css/smoke.using.mjs +++ b/experiments/css/smoke.using.mjs @@ -7,33 +7,39 @@ import assert from 'node:assert/strict'; import { component, compile, decodeIr } from './src/index.js'; const tree = [ - { tag: 'root', classes: ['root'], width: 400, height: 100, state: 0 }, - { tag: 'card', classes: ['card'] }, + { tag: 'root', classes: ['root'], width: 400, height: 100, state: 0 }, + { tag: 'card', classes: ['card'] }, ]; const parents = [0xffffffff, 0]; -const env = { viewportWidth: 400, viewportHeight: 300, colorScheme: 'dark', reducedMotion: false, resolution: 1 }; +const env = { + viewportWidth: 400, + viewportHeight: 300, + colorScheme: 'dark', + reducedMotion: false, + resolution: 1, +}; // Sync `using`: compile prepares and returns the Sheet; released at block exit. { - using sheet = compile('.card { color: red }'); - assert(sheet.diagnostics.length === 0); - assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); - assert(sheet.rules.length > 0); - assert(decodeIr(sheet).version === 2); + using sheet = compile('.card { color: red }'); + assert(sheet.diagnostics.length === 0); + assert(sheet.compute(tree, { parents, target: 1, env }).some(([name]) => name === 'color')); + assert(sheet.rules.length > 0); + assert(decodeIr(sheet).version === 2); } const pagesAfterSync = component.raw.memory.buffer.byteLength; // `await using` also works — release is synchronous but the protocol accepts it. { - await using sheet = compile('.card { color: blue }'); - assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); + await using sheet = compile('.card { color: blue }'); + assert(sheet.compute(tree, { parents, target: 1, env }).some(([name]) => name === 'color')); } // Neither sheet leaked: a third round still works and memory has not grown // beyond the sheets we hold open. { - using sheet = compile('.card { color: green }'); - assert(sheet.compute(tree, parents, 1, env).some(([name]) => name === 'color')); + using sheet = compile('.card { color: green }'); + assert(sheet.compute(tree, { parents, target: 1, env }).some(([name]) => name === 'color')); } assert(component.raw.memory.buffer.byteLength <= pagesAfterSync + 65536 * 4); diff --git a/experiments/css/src/abi.js b/experiments/css/src/abi.js index 6314720..5dc68d0 100644 --- a/experiments/css/src/abi.js +++ b/experiments/css/src/abi.js @@ -10,295 +10,287 @@ const decoder = new TextDecoder(); export const NO_PARENT = 0xffffffff; export async function loadCssComponent(source) { - const bytes = source instanceof Uint8Array ? source : new Uint8Array(await source.arrayBuffer()); - const instance = (await WebAssembly.instantiate(bytes, {})).instance; - return createComponent(instance); + const bytes = source instanceof Uint8Array ? source : new Uint8Array(await source.arrayBuffer()); + const instance = (await WebAssembly.instantiate(bytes, {})).instance; + return createComponent(instance); } export function loadCssComponentFromModule(module) { - return createComponent(new WebAssembly.Instance(module, {})); + return createComponent(new WebAssembly.Instance(module, {})); } function createComponent(instance) { - const exports = instance.exports; - let memory = new Uint8Array(exports.memory.buffer); - let view = new DataView(exports.memory.buffer); - const realloc = exports.cabi_realloc; - const compileFn = exports['bombshell:css/stylesheet@0.4.0#compile']; - const computeFn = exports['bombshell:css/stylesheet@0.4.0#compute']; - const prepareFn = exports['bombshell:css/stylesheet@0.4.0#prepare']; - const releaseFn = exports['bombshell:css/stylesheet@0.4.0#release']; - const computeTreeFn = exports['bombshell:css/stylesheet@0.4.0#compute-tree']; - const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute-tree']; - const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compile']; - const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute']; - function refresh() { - memory = new Uint8Array(exports.memory.buffer); - view = new DataView(exports.memory.buffer); - } - - // Host allocations use `cabi_realloc`: wit-bindgen lifts string/list - // arguments zero-copy (it takes ownership of the buffer and frees it with - // its own allocator), so inputs must come from the allocator the callee - // will free with, and must NOT be freed host-side. Callee-owned return - // areas are released by the `cabi_post_*` hooks after the host reads. - function allocate(bytes) { - const pointer = realloc(0, 0, 1, bytes.byteLength); - refresh(); - memory.set(bytes, pointer); - return pointer; - } - - function writeString(text) { - return allocate(encoder.encode(text)); - } - - function readString(pointer, length) { - return decoder.decode(memory.subarray(pointer, pointer + length)); - } - - function u32(pointer) { - return view.getUint32(pointer, true); - } - - function f32(pointer) { - return view.getFloat32(pointer, true); - } - - function stringPointer(text) { - const bytes = encoder.encode(text); - return { pointer: allocate(bytes), length: bytes.byteLength }; - } - - function stringListPointer(items) { - if (items.length === 0) { - return { pointer: 0, count: 0 }; - } - const entries = items.map(stringPointer); - const pointer = realloc(0, 0, 4, entries.length * 8); - refresh(); - entries.forEach((entry, i) => { - view.setUint32(pointer + i * 8, entry.pointer, true); - view.setUint32(pointer + i * 8 + 4, entry.length, true); - }); - return { pointer, count: entries.length }; - } - - function attributeListPointer(attributes) { - if (attributes.length === 0) { - return { pointer: 0, count: 0 }; - } - const entries = attributes.map((attribute) => ({ - name: stringPointer(attribute.name), - value: stringPointer(attribute.value), - })); - const pointer = realloc(0, 0, 4, entries.length * 16); - refresh(); - entries.forEach((entry, i) => { - const base = pointer + i * 16; - view.setUint32(base, entry.name.pointer, true); - view.setUint32(base + 4, entry.name.length, true); - view.setUint32(base + 8, entry.value.pointer, true); - view.setUint32(base + 12, entry.value.length, true); - }); - return { pointer, count: entries.length }; - } - - function u32ListPointer(values) { - if (values.length === 0) { - return { pointer: 0, count: 0 }; - } - const pointer = realloc(0, 0, 4, values.length * 4); - refresh(); - values.forEach((value, i) => { - view.setUint32(pointer + i * 4, value, true); - }); - return { pointer, count: values.length }; - } - - /// Serialize the flat node list for `compute`/`compute-tree`: - /// tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), - /// classes list (ptr@20, len@24), attributes list - /// (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), - /// height option (disc@48, f32@52), style option - /// (disc@56, ptr@60, len@64) — 68 bytes per node. - function serializeNodes(nodes) { - const stride = 68; - const data = new Uint8Array(nodes.length * stride); - const dataView = new DataView(data.buffer); - nodes.forEach((node, i) => { - const base = i * stride; - const tag = stringPointer(node.tag); - dataView.setUint32(base, tag.pointer, true); - dataView.setUint32(base + 4, tag.length, true); - const id = node.id ? stringPointer(node.id) : null; - dataView.setUint32(base + 8, id ? 1 : 0, true); - if (id) { - dataView.setUint32(base + 12, id.pointer, true); - dataView.setUint32(base + 16, id.length, true); - } - const classes = stringListPointer(node.classes ?? []); - dataView.setUint32(base + 20, classes.pointer, true); - dataView.setUint32(base + 24, classes.count, true); - const attributes = attributeListPointer(node.attributes ?? []); - dataView.setUint32(base + 28, attributes.pointer, true); - dataView.setUint32(base + 32, attributes.count, true); - dataView.setUint8(base + 36, node.state ?? 0); - const hasWidth = node.width !== undefined && node.width !== null; - const hasHeight = node.height !== undefined && node.height !== null; - dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); - dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); - dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); - dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); - const style = node.style ? stringPointer(node.style) : null; - dataView.setUint8(base + 56, style ? 1 : 0); - if (style) { - dataView.setUint32(base + 60, style.pointer, true); - dataView.setUint32(base + 64, style.length, true); - } - }); - return allocate(data); - } - - function compile(css, origin = 2) { - const bytes = encoder.encode(css); - const cssPointer = allocate(bytes); - const result = compileFn(cssPointer, bytes.byteLength, origin); - refresh(); - // compiled-sheet: option> (disc u8@0, ptr@4, len@8) - // + list (ptr@12, len@16) - // The option discriminant is one byte; bytes 1..3 are padding the - // canonical ABI leaves undefined, so they must not leak into the check. - const hasIr = view.getUint8(result) === 1; - const irPointer = u32(result + 4); - const irLength = u32(result + 8); - const diagnosticsPointer = u32(result + 12); - const diagnosticsCount = u32(result + 16); - const ir = hasIr ? memory.slice(irPointer, irPointer + irLength) : null; - const diagnostics = []; - for (let i = 0; i < diagnosticsCount; i++) { - // diagnostic: severity u8@0, code (ptr@4, len@8), message (ptr@12, len@16) - const base = diagnosticsPointer + i * 16; - diagnostics.push({ - severity: view.getUint8(base), - code: readString(u32(base + 4), u32(base + 8)), - message: readString(u32(base + 12), u32(base + 16)), - }); - } - // The callee-allocated return area (and the strings it points to) is - // released by the post-return hook, after the host has finished reading. - postCompile(result); - return { ir, diagnostics }; - } - - /// Decode and parse IR once; returns a handle for `compute`. - function prepare(ir) { - const irBytes = ir instanceof Uint8Array ? ir : new Uint8Array(ir); - const irPointer = allocate(irBytes); - const result = prepareFn(irPointer, irBytes.byteLength); - refresh(); - // option: disc u8@0, value@4 — discriminant is one byte; the - // canonical ABI leaves bytes 1..3 undefined, so read only the byte. - return view.getUint8(result) === 1 ? u32(result + 4) : null; - } - - /// Resolve computed styles for every node in one pass. Returns one - /// declaration list per node, index-aligned with `nodes`. - function computeTree(sheets, nodes, parents, env) { - const handleList = u32ListPointer(sheets); - const nodesPointer = serializeNodes(nodes); - - const parentBytes = new Uint8Array(parents.length * 4); - const parentView = new DataView(parentBytes.buffer); - parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); - const parentPointer = allocate(parentBytes); - - const result = computeTreeFn( - handleList.pointer, - handleList.count, - nodesPointer, - nodes.length, - parentPointer, - parents.length, - env?.viewportWidth != null ? 1 : 0, - env?.viewportWidth ?? 0, - env?.viewportHeight != null ? 1 : 0, - env?.viewportHeight ?? 0, - env?.colorScheme === 'dark' ? 1 : 0, - env?.reducedMotion ? 1 : 0, - env?.resolution ?? 1, - ); - refresh(); - // list>: (ptr@0, len@4); each entry is - // (ptr@0, len@4) over 16-byte name/value pairs. - const outer = u32(result); - const outerCount = u32(result + 4); - const perNode = []; - for (let i = 0; i < outerCount; i++) { - const entryBase = outer + i * 8; - const pointer = u32(entryBase); - const count = u32(entryBase + 4); - const declarations = []; - for (let j = 0; j < count; j++) { - const base = pointer + j * 16; - declarations.push([ - readString(u32(base), u32(base + 4)), - readString(u32(base + 8), u32(base + 12)), - ]); - } - perNode.push(declarations); - } - postComputeTree(result); - return perNode; - } - - function release(sheet) { - releaseFn(sheet); - } - - function compute(sheets, nodes, parents, target, env) { - const handleList = u32ListPointer(sheets); - const nodesPointer = serializeNodes(nodes); - - const parentBytes = new Uint8Array(parents.length * 4); - const parentView = new DataView(parentBytes.buffer); - parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); - const parentPointer = allocate(parentBytes); - - const result = computeFn( - handleList.pointer, - handleList.count, - nodesPointer, - nodes.length, - parentPointer, - parents.length, - target, - env?.viewportWidth != null ? 1 : 0, - env?.viewportWidth ?? 0, - env?.viewportHeight != null ? 1 : 0, - env?.viewportHeight ?? 0, - env?.colorScheme === 'dark' ? 1 : 0, - env?.reducedMotion ? 1 : 0, - env?.resolution ?? 1, - ); - refresh(); - // list: (ptr@0, len@4) - const pointer = u32(result); - const count = u32(result + 4); - const declarations = []; - for (let i = 0; i < count; i++) { - // computed-property: name (ptr@0, len@4), value (ptr@8, len@12) - const base = pointer + i * 16; - declarations.push([ - readString(u32(base), u32(base + 4)), - readString(u32(base + 8), u32(base + 12)), - ]); - } - postCompute(result); - return declarations; - } - - return { compile, prepare, compute, computeTree, release, raw: exports }; + const exports = instance.exports; + let memory = new Uint8Array(exports.memory.buffer); + let view = new DataView(exports.memory.buffer); + const realloc = exports.cabi_realloc; + const compileFn = exports['bombshell:css/stylesheet@0.4.0#compile']; + const computeFn = exports['bombshell:css/stylesheet@0.4.0#compute']; + const prepareFn = exports['bombshell:css/stylesheet@0.4.0#prepare']; + const releaseFn = exports['bombshell:css/stylesheet@0.4.0#release']; + const computeTreeFn = exports['bombshell:css/stylesheet@0.4.0#compute-tree']; + const postComputeTree = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute-tree']; + const postCompile = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compile']; + const postCompute = exports['cabi_post_bombshell:css/stylesheet@0.4.0#compute']; + function refresh() { + memory = new Uint8Array(exports.memory.buffer); + view = new DataView(exports.memory.buffer); + } + + // Host allocations use `cabi_realloc`: wit-bindgen lifts string/list + // arguments zero-copy (it takes ownership of the buffer and frees it with + // its own allocator), so inputs must come from the allocator the callee + // will free with, and must NOT be freed host-side. Callee-owned return + // areas are released by the `cabi_post_*` hooks after the host reads. + function allocate(bytes) { + const pointer = realloc(0, 0, 1, bytes.byteLength); + refresh(); + memory.set(bytes, pointer); + return pointer; + } + + function readString(pointer, length) { + return decoder.decode(memory.subarray(pointer, pointer + length)); + } + + function u32(pointer) { + return view.getUint32(pointer, true); + } + + function stringPointer(text) { + const bytes = encoder.encode(text); + return { pointer: allocate(bytes), length: bytes.byteLength }; + } + + function stringListPointer(items) { + if (items.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = items.map(stringPointer); + const pointer = realloc(0, 0, 4, entries.length * 8); + refresh(); + entries.forEach((entry, i) => { + view.setUint32(pointer + i * 8, entry.pointer, true); + view.setUint32(pointer + i * 8 + 4, entry.length, true); + }); + return { pointer, count: entries.length }; + } + + function attributeListPointer(attributes) { + if (attributes.length === 0) { + return { pointer: 0, count: 0 }; + } + const entries = attributes.map((attribute) => ({ + name: stringPointer(attribute.name), + value: stringPointer(attribute.value), + })); + const pointer = realloc(0, 0, 4, entries.length * 16); + refresh(); + entries.forEach((entry, i) => { + const base = pointer + i * 16; + view.setUint32(base, entry.name.pointer, true); + view.setUint32(base + 4, entry.name.length, true); + view.setUint32(base + 8, entry.value.pointer, true); + view.setUint32(base + 12, entry.value.length, true); + }); + return { pointer, count: entries.length }; + } + + function u32ListPointer(values) { + if (values.length === 0) { + return { pointer: 0, count: 0 }; + } + const pointer = realloc(0, 0, 4, values.length * 4); + refresh(); + values.forEach((value, i) => { + view.setUint32(pointer + i * 4, value, true); + }); + return { pointer, count: values.length }; + } + + /// Serialize the flat node list for `compute`/`compute-tree`: + /// tag (ptr@0, len@4), id option (disc@8, ptr@12, len@16), + /// classes list (ptr@20, len@24), attributes list + /// (ptr@28, len@32), state u8@36, width option (disc@40, f32@44), + /// height option (disc@48, f32@52), style option + /// (disc@56, ptr@60, len@64) — 68 bytes per node. + function serializeNodes(nodes) { + const stride = 68; + const data = new Uint8Array(nodes.length * stride); + const dataView = new DataView(data.buffer); + nodes.forEach((node, i) => { + const base = i * stride; + const tag = stringPointer(node.tag); + dataView.setUint32(base, tag.pointer, true); + dataView.setUint32(base + 4, tag.length, true); + const id = node.id ? stringPointer(node.id) : null; + dataView.setUint32(base + 8, id ? 1 : 0, true); + if (id) { + dataView.setUint32(base + 12, id.pointer, true); + dataView.setUint32(base + 16, id.length, true); + } + const classes = stringListPointer(node.classes ?? []); + dataView.setUint32(base + 20, classes.pointer, true); + dataView.setUint32(base + 24, classes.count, true); + const attributes = attributeListPointer(node.attributes ?? []); + dataView.setUint32(base + 28, attributes.pointer, true); + dataView.setUint32(base + 32, attributes.count, true); + dataView.setUint8(base + 36, node.state ?? 0); + const hasWidth = node.width !== undefined && node.width !== null; + const hasHeight = node.height !== undefined && node.height !== null; + dataView.setUint32(base + 40, hasWidth ? 1 : 0, true); + dataView.setFloat32(base + 44, hasWidth ? node.width : 0, true); + dataView.setUint32(base + 48, hasHeight ? 1 : 0, true); + dataView.setFloat32(base + 52, hasHeight ? node.height : 0, true); + const style = node.style ? stringPointer(node.style) : null; + dataView.setUint8(base + 56, style ? 1 : 0); + if (style) { + dataView.setUint32(base + 60, style.pointer, true); + dataView.setUint32(base + 64, style.length, true); + } + }); + return allocate(data); + } + + function compile(css, origin = 2) { + const bytes = encoder.encode(css); + const cssPointer = allocate(bytes); + const result = compileFn(cssPointer, bytes.byteLength, origin); + refresh(); + // compiled-sheet: option> (disc u8@0, ptr@4, len@8) + // + list (ptr@12, len@16) + // The option discriminant is one byte; bytes 1..3 are padding the + // canonical ABI leaves undefined, so they must not leak into the check. + const hasIr = view.getUint8(result) === 1; + const irPointer = u32(result + 4); + const irLength = u32(result + 8); + const diagnosticsPointer = u32(result + 12); + const diagnosticsCount = u32(result + 16); + const ir = hasIr ? memory.slice(irPointer, irPointer + irLength) : null; + const diagnostics = []; + for (let i = 0; i < diagnosticsCount; i++) { + // diagnostic: severity u8@0, code (ptr@4, len@8), message (ptr@12, len@16) + const base = diagnosticsPointer + i * 16; + diagnostics.push({ + severity: view.getUint8(base), + code: readString(u32(base + 4), u32(base + 8)), + message: readString(u32(base + 12), u32(base + 16)), + }); + } + // The callee-allocated return area (and the strings it points to) is + // released by the post-return hook, after the host has finished reading. + postCompile(result); + return { ir, diagnostics }; + } + + /// Decode and parse IR once; returns a handle for `compute`. + function prepare(ir) { + const irBytes = ir instanceof Uint8Array ? ir : new Uint8Array(ir); + const irPointer = allocate(irBytes); + const result = prepareFn(irPointer, irBytes.byteLength); + refresh(); + // option: disc u8@0, value@4 — discriminant is one byte; the + // canonical ABI leaves bytes 1..3 undefined, so read only the byte. + return view.getUint8(result) === 1 ? u32(result + 4) : null; + } + + /// Resolve computed styles for every node in one pass. Returns one + /// declaration list per node, index-aligned with `nodes`. + function computeTree({ sheets, nodes, parents, env }) { + const handleList = u32ListPointer(sheets); + const nodesPointer = serializeNodes(nodes); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeTreeFn( + handleList.pointer, + handleList.count, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list>: (ptr@0, len@4); each entry is + // (ptr@0, len@4) over 16-byte name/value pairs. + const outer = u32(result); + const outerCount = u32(result + 4); + const perNode = []; + for (let i = 0; i < outerCount; i++) { + const entryBase = outer + i * 8; + const pointer = u32(entryBase); + const count = u32(entryBase + 4); + const declarations = []; + for (let j = 0; j < count; j++) { + const base = pointer + j * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + perNode.push(declarations); + } + postComputeTree(result); + return perNode; + } + + function release(sheet) { + releaseFn(sheet); + } + + function compute({ sheets, nodes, parents, target, env }) { + const handleList = u32ListPointer(sheets); + const nodesPointer = serializeNodes(nodes); + + const parentBytes = new Uint8Array(parents.length * 4); + const parentView = new DataView(parentBytes.buffer); + parents.forEach((parent, i) => parentView.setUint32(i * 4, parent, true)); + const parentPointer = allocate(parentBytes); + + const result = computeFn( + handleList.pointer, + handleList.count, + nodesPointer, + nodes.length, + parentPointer, + parents.length, + target, + env?.viewportWidth != null ? 1 : 0, + env?.viewportWidth ?? 0, + env?.viewportHeight != null ? 1 : 0, + env?.viewportHeight ?? 0, + env?.colorScheme === 'dark' ? 1 : 0, + env?.reducedMotion ? 1 : 0, + env?.resolution ?? 1, + ); + refresh(); + // list: (ptr@0, len@4) + const pointer = u32(result); + const count = u32(result + 4); + const declarations = []; + for (let i = 0; i < count; i++) { + // computed-property: name (ptr@0, len@4), value (ptr@8, len@12) + const base = pointer + i * 16; + declarations.push([ + readString(u32(base), u32(base + 4)), + readString(u32(base + 8), u32(base + 12)), + ]); + } + postCompute(result); + return declarations; + } + + return { compile, prepare, compute, computeTree, release, raw: exports }; } // --------------------------------------------------------------------------- @@ -306,173 +298,217 @@ function createComponent(instance) { // --------------------------------------------------------------------------- export function decodeIr(bytes) { - const reader = new IrReader(bytes); - return { - version: reader.varint(), - origin: reader.byte(), - rules: reader.vec(() => reader.rule()), - layerOrder: reader.vec(() => reader.stringVec()), - keyframes: reader.vec(() => ({ name: reader.string(), body: reader.string() })), - opaque: reader.vec(() => ({ text: reader.string() })), - warnings: reader.vec(() => reader.diagnostic()), - }; + const reader = new IrReader(bytes); + return { + version: reader.varint(), + origin: reader.byte(), + rules: reader.vec(() => reader.rule()), + layerOrder: reader.vec(() => reader.stringVec()), + keyframes: reader.vec(() => ({ name: reader.string(), body: reader.string() })), + opaque: reader.vec(() => ({ text: reader.string() })), + warnings: reader.vec(() => reader.diagnostic()), + }; +} + +/// Thrown when the IR decoder encounters a feature kind it doesn't know. +export class IrDecodeError extends Error { + constructor(message) { + super(message); + this.name = 'IrDecodeError'; + } } class IrReader { - constructor(bytes) { - this.bytes = bytes; - this.cursor = 0; - this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); - } - - byte() { - return this.bytes[this.cursor++]; - } - - varint() { - let value = 0; - let shift = 0; - for (;;) { - const byte = this.bytes[this.cursor++]; - value |= (byte & 0x7f) << shift; - if ((byte & 0x80) === 0) { - break; - } - shift += 7; - } - return value >>> 0; - } - - zigzag() { - const n = this.varint(); - return (n >>> 1) ^ -(n & 1); - } - - string() { - const length = this.varint(); - const text = decoder.decode(this.bytes.subarray(this.cursor, this.cursor + length)); - this.cursor += length; - return text; - } - - stringVec() { - return this.vec(() => this.string()); - } - - vec(read) { - const count = this.varint(); - const items = []; - for (let i = 0; i < count; i++) { - items.push(read()); - } - return items; - } - - diagnostic() { - return { severity: this.byte(), code: this.string(), message: this.string() }; - } - - rule() { - return { - selector: this.string(), - specificity: this.varint(), - layer: this.option(() => this.stringVec()), - media: this.vec(() => this.mediaCondition()), - containers: this.vec(() => ({ - name: this.option(() => this.string()), - condition: this.containerCondition(), - })), - supports: this.vec(() => this.supportsCondition()), - declarations: this.vec(() => ({ - name: this.string(), - value: this.string(), - important: this.byte() === 1, - })), - order: this.varint(), - }; - } - - option(read) { - if (this.byte() === 0) { - return null; - } - return read(); - } - - mediaCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'feature', feature: this.feature() }; - case 1: return { type: 'not', inner: this.mediaCondition() }; - case 2: return { type: 'and', conditions: this.vec(() => this.mediaCondition()) }; - case 3: return { type: 'or', conditions: this.vec(() => this.mediaCondition()) }; - default: return { type: 'unknown' }; - } - } - - feature() { - const name = this.string(); - const kind = this.varint(); - switch (kind) { - case 0: return { name, kind: { type: 'plain', value: this.featureValue() } }; - case 1: return { name, kind: { type: 'boolean' } }; - case 2: return { name, kind: { type: 'range', cmp: this.varint(), value: this.featureValue() } }; - case 3: return { - name, - kind: { - type: 'interval', - start: this.featureValue(), - startCmp: this.varint(), - end: this.featureValue(), - endCmp: this.varint(), - }, - }; - default: throw new Error(`unknown feature kind ${kind}`); - } - } - - featureValue() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'px', value: this.f32() }; - case 1: return { type: 'length', value: this.f32(), unit: this.string() }; - case 2: return { type: 'number', value: this.f32() }; - case 3: return { type: 'integer', value: this.zigzag() }; - case 4: return { type: 'boolean', value: this.byte() === 1 }; - case 5: return { type: 'dppx', value: this.f32() }; - case 6: return { type: 'ratio', a: this.f32(), b: this.f32() }; - case 7: return { type: 'ident', value: this.string() }; - default: return { type: 'unknown' }; - } - } - - f32() { - const value = this.view.getFloat32(this.cursor, true); - this.cursor += 4; - return value; - } - - containerCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'size', feature: this.feature() }; - case 1: return { type: 'style', text: this.string() }; - case 2: return { type: 'not', inner: this.containerCondition() }; - case 3: return { type: 'and', conditions: this.vec(() => this.containerCondition()) }; - case 4: return { type: 'or', conditions: this.vec(() => this.containerCondition()) }; - default: return { type: 'unknown' }; - } - } - - supportsCondition() { - const tag = this.varint(); - switch (tag) { - case 0: return { type: 'declaration', name: this.string(), value: this.string(), supported: this.byte() === 1 }; - case 1: return { type: 'selector', text: this.string(), supported: this.byte() === 1 }; - case 2: return { type: 'not', inner: this.supportsCondition() }; - case 3: return { type: 'and', conditions: this.vec(() => this.supportsCondition()) }; - case 4: return { type: 'or', conditions: this.vec(() => this.supportsCondition()) }; - default: return { type: 'unknown' }; - } - } + constructor(bytes) { + this.bytes = bytes; + this.cursor = 0; + this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + } + + byte() { + return this.bytes[this.cursor++]; + } + + varint() { + let value = 0; + let shift = 0; + for (;;) { + const byte = this.bytes[this.cursor++]; + value |= (byte & 0x7f) << shift; + if ((byte & 0x80) === 0) { + break; + } + shift += 7; + } + return value >>> 0; + } + + zigzag() { + const n = this.varint(); + return (n >>> 1) ^ -(n & 1); + } + + string() { + const length = this.varint(); + const text = decoder.decode(this.bytes.subarray(this.cursor, this.cursor + length)); + this.cursor += length; + return text; + } + + stringVec() { + return this.vec(() => this.string()); + } + + vec(read) { + const count = this.varint(); + const items = []; + for (let i = 0; i < count; i++) { + items.push(read()); + } + return items; + } + + diagnostic() { + return { severity: this.byte(), code: this.string(), message: this.string() }; + } + + rule() { + return { + selector: this.string(), + specificity: this.varint(), + layer: this.option(() => this.stringVec()), + media: this.vec(() => this.mediaCondition()), + containers: this.vec(() => ({ + name: this.option(() => this.string()), + condition: this.containerCondition(), + })), + supports: this.vec(() => this.supportsCondition()), + declarations: this.vec(() => ({ + name: this.string(), + value: this.string(), + important: this.byte() === 1, + })), + order: this.varint(), + }; + } + + option(read) { + if (this.byte() === 0) { + return null; + } + return read(); + } + + mediaCondition() { + const tag = this.varint(); + switch (tag) { + case 0: + return { type: 'feature', feature: this.feature() }; + case 1: + return { type: 'not', inner: this.mediaCondition() }; + case 2: + return { type: 'and', conditions: this.vec(() => this.mediaCondition()) }; + case 3: + return { type: 'or', conditions: this.vec(() => this.mediaCondition()) }; + default: + return { type: 'unknown' }; + } + } + + feature() { + const name = this.string(); + const kind = this.varint(); + switch (kind) { + case 0: + return { name, kind: { type: 'plain', value: this.featureValue() } }; + case 1: + return { name, kind: { type: 'boolean' } }; + case 2: + return { name, kind: { type: 'range', cmp: this.varint(), value: this.featureValue() } }; + case 3: + return { + name, + kind: { + type: 'interval', + start: this.featureValue(), + startCmp: this.varint(), + end: this.featureValue(), + endCmp: this.varint(), + }, + }; + default: + throw new IrDecodeError(`unknown feature kind ${kind}`); + } + } + + featureValue() { + const tag = this.varint(); + switch (tag) { + case 0: + return { type: 'px', value: this.f32() }; + case 1: + return { type: 'length', value: this.f32(), unit: this.string() }; + case 2: + return { type: 'number', value: this.f32() }; + case 3: + return { type: 'integer', value: this.zigzag() }; + case 4: + return { type: 'boolean', value: this.byte() === 1 }; + case 5: + return { type: 'dppx', value: this.f32() }; + case 6: + return { type: 'ratio', a: this.f32(), b: this.f32() }; + case 7: + return { type: 'ident', value: this.string() }; + default: + return { type: 'unknown' }; + } + } + + f32() { + const value = this.view.getFloat32(this.cursor, true); + this.cursor += 4; + return value; + } + + containerCondition() { + const tag = this.varint(); + switch (tag) { + case 0: + return { type: 'size', feature: this.feature() }; + case 1: + return { type: 'style', text: this.string() }; + case 2: + return { type: 'not', inner: this.containerCondition() }; + case 3: + return { type: 'and', conditions: this.vec(() => this.containerCondition()) }; + case 4: + return { type: 'or', conditions: this.vec(() => this.containerCondition()) }; + default: + return { type: 'unknown' }; + } + } + + supportsCondition() { + const tag = this.varint(); + switch (tag) { + case 0: + return { + type: 'declaration', + name: this.string(), + value: this.string(), + supported: this.byte() === 1, + }; + case 1: + return { type: 'selector', text: this.string(), supported: this.byte() === 1 }; + case 2: + return { type: 'not', inner: this.supportsCondition() }; + case 3: + return { type: 'and', conditions: this.vec(() => this.supportsCondition()) }; + case 4: + return { type: 'or', conditions: this.vec(() => this.supportsCondition()) }; + default: + return { type: 'unknown' }; + } + } } diff --git a/experiments/css/src/engine.js b/experiments/css/src/engine.js index b7991fe..1567b73 100644 --- a/experiments/css/src/engine.js +++ b/experiments/css/src/engine.js @@ -56,18 +56,18 @@ export class Engine { } /// Computed style for one node across all sheets. - compute(nodes, parents, target, env) { + compute(nodes, { parents, target, env } = {}) { const handles = this.#handles(); if (handles.length === 0) return []; - return component.compute(handles, nodes, parents, target, env); + return component.compute({ sheets: handles, nodes, parents, target, env }); } /// Computed styles for every node in one pass, index-aligned with /// `nodes`. - computeTree(nodes, parents, env) { + computeTree(nodes, { parents, env } = {}) { const handles = this.#handles(); if (handles.length === 0) return nodes.map(() => []); - return component.computeTree(handles, nodes, parents, env); + return component.computeTree({ sheets: handles, nodes, parents, env }); } /// Release every prepared sheet. A later `compute` re-prepares from the diff --git a/experiments/css/src/index.js b/experiments/css/src/index.js index 64f871f..1b717a6 100644 --- a/experiments/css/src/index.js +++ b/experiments/css/src/index.js @@ -19,9 +19,9 @@ // // The embedded artifact decodes via `node:zlib`, so it targets Node-like // runtimes; browser hosts load the raw module and use `loadCssComponent`. -import { compiled } from "./wasm.js"; -import { decodeIr as decodeIrBytes, loadCssComponentFromModule } from "./abi.js"; -export { NO_PARENT, loadCssComponent, loadCssComponentFromModule } from "./abi.js"; +import { compiled } from './wasm.js'; +import { decodeIr as decodeIrBytes, loadCssComponentFromModule } from './abi.js'; +export { NO_PARENT, loadCssComponent, loadCssComponentFromModule } from './abi.js'; export { compiled }; export const component = loadCssComponentFromModule(compiled); @@ -36,17 +36,17 @@ export function compile(css, origin = 2) { } const handle = component.prepare(ir); if (handle === null) { - throw new Error("prepare rejected the IR"); + throw new PrepareError(); } - return new Sheet(handle, ir, diagnostics); + return new Sheet({ handle, ir, diagnostics }); } -export function compute(sheet, nodes, parents, target, env) { - return component.compute([handleOf(sheet)], nodes, parents, target, env); +export function compute(sheet, { nodes, parents, target, env } = {}) { + return component.compute({ sheets: [handleOf(sheet)], nodes, parents, target, env }); } -export function computeTree(sheet, nodes, parents, env) { - return component.computeTree([handleOf(sheet)], nodes, parents, env); +export function computeTree(sheet, { nodes, parents, env } = {}) { + return component.computeTree({ sheets: [handleOf(sheet)], nodes, parents, env }); } export function release(sheet) { @@ -71,16 +71,24 @@ function handleOf(sheet) { /// severity/code/message list from the engine. export class CompileError extends Error { constructor(diagnostics) { - super("compile returned no IR"); - this.name = "CompileError"; + super('compile returned no IR'); + this.name = 'CompileError'; this.diagnostics = diagnostics; } } +/// Thrown when `prepare` rejects valid-looking IR bytes. +export class PrepareError extends Error { + constructor() { + super('prepare rejected the IR'); + this.name = 'PrepareError'; + } +} + /// A prepared stylesheet. Wraps the raw handle so calls don't repeat it, and /// releases itself at scope exit: `using sheet = compile(css)`. export class Sheet { - constructor(handle, ir, diagnostics) { + constructor({ handle, ir, diagnostics }) { this.handle = handle; this.ir = ir; this.diagnostics = diagnostics; @@ -96,12 +104,18 @@ export class Sheet { return this.#decodedIr().rules; } - compute(nodes, parents, target, env) { - return component.compute([this.handle], nodes, parents, target, env); + compute(nodes, { parents, target, env } = {}) { + return component.compute({ + sheets: [this.handle], + nodes, + parents, + target, + env, + }); } - computeTree(nodes, parents, env) { - return component.computeTree([this.handle], nodes, parents, env); + computeTree(nodes, { parents, env } = {}) { + return component.computeTree({ sheets: [this.handle], nodes, parents, env }); } release() { diff --git a/experiments/css/src/wasm.js b/experiments/css/src/wasm.js index 4568bfb..369f795 100644 --- a/experiments/css/src/wasm.js +++ b/experiments/css/src/wasm.js @@ -1,8 +1,28 @@ -import{brotliDecompressSync}from"node:zlib"; -const Z="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"; -const T=new Uint8Array(128);for(let i=0;i<85;i++)T[Z.charCodeAt(i)]=i; +import { brotliDecompressSync } from 'node:zlib'; +const Z = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#'; +const T = new Uint8Array(128); +for (let i = 0; i < 85; i++) T[Z.charCodeAt(i)] = i; // the table lookups are non-null for well-formed z85 input; the bounds // checks below handle a short tail, so the assertions carry no risk -function d(s,n){const b=new Uint8Array(n);let o=0;for(let i=0;iw:MWiu31%mbFXCaF=7!RTi=6]RIkrIbS}1TaYJPi(^vq$LZdBpduom-=JyjY9horYjk-9G[B-bv4^W-vS4!M8wvllH}nyw$VYR#!cX81}rb9J}g%4vWxxVdk2Q#m>Q^)E!%9h7WlRUa7/+OrL*w[i#+#z.u8vFyYIIfL=mG!jYLHTb?-eVkES(K?toasn0RiJSqZm*DW[K5FqE.n2adKY}atr/J$BmAUT>*LIK0Tyr])x.a%Kv[-I^7#gzFn4}}+FuMo*OP@}Dx*a-OTeBJYRIVX[0Y}*Le{S]u6/Ca[/#Zp)ZO/OyPQbfFa7Pwf1vL]kXMg!s88wmuYtb5=#+NtZCsrqDX-cntZgGeAJaE9HiP{UxiHS{mpS0u.8kkJ9tYIFN-Cy!qNvM$oNVr^JB$a5TwqPYg+L):U/?7#6+(impA*IBeF9fe.FQGE0y62d[(R)XBu.ao->!>k5s{a:T9OIQ$)m8h)MW--r8X=6Y?ZOKKauG}UkjG1TuvR0Sen1vF]/oUe^U:k3bu=FAa9SyV/1x/1id121dM:-sv[rp(/hH}1YspJL47N&iLIT?Zj2}FhtaX[9+}>ehW%QTsHyeQ!DXt1llz9e)KxRrQ1v3eu@mg#9]3A#mT/2r:ORW!ON/pFi8{qzPO7]3N#Q.b9V9K@zKD810QM(FLcayp-tnO{&*to)VLkg&=XVoS0PRmT3!D?dgoX6*fOt1[LW8h*z-#=9Ec/qE7oD0?K*/vlc8+IjI05ob]CKtn<-r5b1}QvS>9L](!9>uD6^F?nW-n6JBslV.v87d+rDxpaB9b/xJ?x<4UQ+?)7:>LL8riCMydQe1JpS{Hd1Tq!Cl^etE3m:36=Fgp3Aly(*8PZC&Ac@{]epg-2q9&Faw{k0}YI]Q8hZ8$q[klglNad%C.08&>CRgCq7cp6Y{PQV4kC7L%Gp0g:j^v5VZ4%r{0+c50tIfot>D*]xHRHa*HT3d]ishM>hD:h?%fUamE-KlPa?T$HhI=zAN}@W.ipGzW)M+(Ud!&y:9F3F}1g><]b7.5S7AqjjLZpNO:4S(d@]&6}j4^:4o=)1f)W*[Pa^GfK1H#%&CS>FA%}:(/mE<-7z}!0BGSxCOmiM[=)]hx-Bg8Qf8@guiCzNqT$1p3=o(h3&XOOT+q)Xo^x:v^UJak>:k*yGIEvhj-#o/A{]/Y%T5Am%#!gEju-1kO)>Kep.&p1nrs}zOH%3]2FEnq{&rP&$2=X4-cc4tVg6LJ4L}.(OuQQQbsEQCkQT5FkSL7?OkhYy&P%o]P7c2-o$7rSg.u:CWIK!?&4R:U2@*Zal+>{i8f[lp.fg+=(@5nU4!%Ct&}:z(VXG-zP0fv5L{BO?oS8LQly%}>JMC=mxg44kW:jR$K>98C:tpxlM^%5tr/S{}0FHJxig}ZGZSU9$!OMS9ux0c&xbP@l?H(IkK.y]X%@{d/=P^*!%Xv8wCTSwxIS}uxfl}?iq+Q{[]lv{}j710ntSmK4Lg6BEZmz-OAyWmK&7}<7BWo/&Kon#KELh+Z[ZJ=[b)(UAf1F5(M!PyN=c0rM{#zJUcuZKdxAvfuQKnr&ntl]daMKR-}/8[3AoueQsVAEke!65pw!0$l.R4a]GMK!7Y?6RdF>PbN(C^k1EkF1Egn5NV7>8<2!(bMOD[>G)]&]u?bd4FG9ZLl@Q4LH^3K*2NMX1s{ZHuJ=lej56+EcX{$eNqIFTw?#PldF0=t%bc>&=TvNI]a#ARw)SZHx4l5nXry5juUZG{ap#:t7Pwzmm1:AN0lhIO030$iNgtB!VN.=$^?2F=S=2(F/fA>maJJw&T1dCQB%@=SoRw4z5A>nrrWAp#fXV[9Vol*C)s=M7rX?PyW1o2&p./:.}U1ApV(WypWr#Z{W?R!%]kCEh-vXe!rK{Q}X6lq%kK%fP(UY4e-cAK?wS(m}kUb5sQMinT4Syy5A){dM-OlgP1=E)lHZi3-XikxH-M6z<34d@Xt]hDlrb9ARkOvQ:*y@MgkPXC#Pqf^R5Ga(A[/]!7xTX>ttV6M3d9j5E])F)9a7).N.?6zC@Jr:#P&vKY$%WVUGrjjh$bQYRA%pfZaTnF@%86@m0(5D$fYJ/[c$OsyYH:TIn0OfqK!iDyY@aqWPUSu.8iMnBY7]xQ/*ChlJ>v6CtX0esChi79:05CpCKC:75W*#%}lgXXggSh{jDc=450w=G?Sye?HYy-p?)y8StL)RSYWy8E@TZnM%$j&1}iQu.[UmjP90ohtv6mQod@)8G49u^@B>*dpZyhY3D&6Ww>j:josB6h:]a6%fFyK-FU{1Tz+b=n*caOtXcc%yM]S3w8EIC>Ip@6R-Sud6J{UsS*3gD+3l}-Rt.u^Pd:^u:KL}R.j!U@:M{s2Gv^ZVH9s$U@a<=/9yXkRK=#^+sTS>RE/8CTq2U[/e%4S@?P)dR1qO]OHfAwr6T05C:H16jq-7=Fd!9N-y>6Aqe6e7+&.R8c2A%}E{GV)*^uXnE128@iZF7P.6{hSUGswZCsU8jM4IhdsrXP3rKsHQ9wlZo?R>x4tg&6Iy.KPBS^1KF*z9g89G=1N.?^I6hZ(&TKAl)vo{!m&u$1Gz+Svbikl3bbqNCo#v0iCAkS6wy}E^R$)yU{}yGTH2i(7-b$?&r/O@^ny#W9sNmMavl<@8@PxtdAf]9?{wxZF4e(cv[cS!ave].qD:d5xJ5D&9]/^!x*=#}fU-bia+=-2huo[MrH0K259U/=?2ZcTwM}Oc>mOc-wn.1Dh@5&{mgVOt}=+a+2o)=bMq?fzUG%^M/8.LtjbL.vW15JkKJ>H{Q&q7U*7atojz=Ho/fS6DIgeu/oixF2i>s8WQ8*mWD(AW{Zg^V0[I^0&Fps4l{ac!]R2xCOXyWG4NK#0Q1-Ex7[PhRukgQ3jXX.vyn2o1Vap$q?FV#8{b(vj()IMte=^%HG?4C(gg@QLL9Gyv9&kc-z=W5gxac0A?-pjCChtpCv0{XjQ-M%S5P@:G+W)RilQm>yW{IrAnF3h7z87dYJ<9r+gR8{A^02l)+r8U*YZzVTnIf06[mI7&97^eB43S2{W+>Ejsf09Xh>T&b%P!G1AJx5QWUtBE-kqzmsJXOTEf88302)esfS>BP)!mvF{x1<+N*Z:Jv%fN@1qdnLp+A3BSNLVxRakz+GIzVQh3-Ed%MHct[Q)kFktab1zg{f(]Y0$WSF0jVa2%n7):OIW]t5f8nK}j&VM<8f585Yd>eP+tpeB7Tfy0DV:UQ#lZw?5Wg%.[8cC!]%.Itlu}S2*kvoL]Oo0YR@[4jF%GOpBX#.3v+$Oq(T=31A^6k+}OY=1X!F&=838%/X{K4/W?T?%5}t=TbO]$j=>ZG3kDG2c(LO^qe})foBprzO#tdpj^*x@6K3SZr]RU4ycG&+Q)O%pn$Z(6>c/[*WM@6fPRezjEQUyb)$P6w6Lyl9tjTPLwQEVwXP9W8BXc$$:I}f3]Enr*AEgEr3vEw@N^IyLiDE<{l{&g&30h>{fqmyOn!Xs?dl#]nXX.*Ll0$RVaZ<3Ip[7%5NN(:SnuG1:Fe5B(KqCZ(c5)Aq!wk}5}M0n/DW[{<}4ga1OgBn17BF!OfPd(Rs^DMRG4E0mv?-ZZAtD5(vAQcMPT&.lM6vT[(v6j>F1cC{BX0O-eb[O@^Mjde<3g6$9t?KI7Rn>rk1#*fH]dMe0Sxs6gNwN[WI{A64)Bl>w6qr-zaAhFxa#xXeVtfKa5WtQDxQs[.4UaK$-y1(Kv0LIICO#)C*LWcASwEtJQ%?r.zumo!:M@W]k{^h?8OcCQaWHs6r?g!DK3s.&!f^45x73NZePV<>qlT]>0ti](qHo6QUUaq!Se6##arXz>aOM$#huhg8KwfX3zZ=>sIMJ8B538EkrYPZ}z$qE<E=M4wPRD54M@&>Os>-0QIau@AMaj%4kf<:?!+C)jCm:%{e58N>Aba!fgR$FmZF38De:%:wH1=tD!yA^/:Vf0/M*XNA1$qfc[G]dT31ROLR@EwX/uCO{7766iI2TZycTc!Dqvufz34?FCJq6%SmHK^*9WP5Teke>FfUKJB0Yp3r)Fd^bbe6AP+yCENu}0^jQw<6RX[{9!5KezqG$o1S?I

Bx(nKj/A$6-2*xijiQGzkNawpMHef9v3+L>h):V8]{l52NFp5>crCEq*#9[6@cCNzB:8/d+}dNL8ctLOjubS/-x>P8XM0AyAETBi+9wLTXZ?zjWA>h1/[>s^{+5IN2@n?ul?[S+$))qcY]A(-yspto?.Q3[ZyZ05D{z4{./u>bVVlO.7U$vXs$uQGa&tevZg!(ZLwrxrRFwqCG}22fN?M9dd.NllE}Wq+1&=7+!BFGe*21!It>gL-a>!wZG@OS]$^dku)W2FFOU9TtDrf7KBDu<7fi[Y3BTl].Iy:@S0MZ5QjaulAW4wb@et{0A.I:0dpyu6$FOL)nSIYuJS?N8Z4>&iBhiWv#L3d7@d9sEDkOWmv]l7s.f.Bc]}(qBD$wOiUu{C8-S)+B7e/=Y<5{gnWmzK0Yg!DKts&]x>g@&/u)g*t5vZIDfy7]sdFg66rI9s!LZ&P=!.v!:LA4^kY.crqQecu=^t^Sxb!X&S6?w[aP{DD.ZsJ@TAY(D}w/J<}bhl}2Lj.P>g+BNEXM4Y0U.f]3!-{jYEiLB9DRA%nz.?3OCT5[!n.-v(!wCdpfD:47MP3<&z$eaci^%BksXkh7^&ZPJQbM])%FF68)&w^3zn%y4}}Z*0s1QgW-/0GOvuuCeW./1aQlGLr/0ELYNcfjlhZxz1RDBN3#t4wW9@rT4av^>d*)U].>#]PW2Y+0T[w3]+y[+UT#Br2{@n0eIASY5nt8h537##c!6API6b.XOCIl)IQ(VXbYa8+!Ga{{lWqidHxvp6JbhulgMa[U.u8suChobyA3z@<5i6n{hL/VPv/.xxP%(o=ydb[I*Z+&879N%:QIIXzQALZA3W%#dr$$./LDj&/sHt)t:n?zrNhPTFPmI7#0hp4ge]5V4CZa9Lnovq>]OXRF8+xo!yuaUm]+xr+a]Zh-:]}roVt.[Ee?.HBvJu1AI(szZ]i)GVPIsrac7!}1T*X5n:5q(-1FhUvm]5Rh!1OXrcXF5Pm1.WI-*GgLIHbNszAD4RNWavKs:0l7uTHg2=9NRdjL4Nzcb4d%n=g?8V(V&))sD8Dx)Jl)>V^ui[@J+*JmV+LkTz]DE7M]Q5-Nolt2!sJV.:7(8exd)>RgpwZ*sr%mK+QnIHX6Vrz}(X1Dg1-23?vXq<}fl-LM7NeHI+&9utjzAUyAmZs*ZelWzNoTz-@OhL2eO=aqqK^Tp{Os>oyPM=g(u)Sp:#pF?2smPaI+xYxo-2>19PE>3(+4)p{Lsm5^?JWU^p*)$16oMVuty%j!}7J:s?mB3.tPzTE.n1hf$?fD]M[e3tWdWZYl?T4g^%8?8lf[X45q<4b]BZ#X2F2sTlB#dGz00>5ZU[:W7fJKd@]Tq81gSci+R(RJcs-JG=h[GoW}Ju4-D#g}e!/.NersnJRV5(vcXa:D%Ed6Kr?gaJ&=b]g*jF48nfuR>tybU^QfAM&v%w2=+[ovV#[d4>s839>ynE4x?2D5&%8lRX[(l:!AxWnGtSBcE:8fbUK](Bt.)U3:sU*H![G!A%KIbWE.fD?]H759wS%It)}ky@SW6O+{u(ES)/WvBXYcOo$T1M}adHDKstPG(ayLKh)BtB{B%()]7oW.5hWbQ^kkuS3m-m^$8o:1jT*ZlNc*#quxBW]/%}3ImOViTN<&RO:y+)L8cgzWJ*d+RbcOGUK.qHN7(5uJ5WfWeKz{!?v.Rsk*zb3B/[3b)EX&-R@GywfYuPmag:GE(o)84MTcFm.H94hbmUEug:HjxktmE0nal&g4cdfym-(*@!Pmnd0w>j+(LU}kWo+=O^!Uu$3!./sLANh}jK)#ytk:({KGXJ}AqJ0FDITZ/qdOHM&%/d-sbYKE{$Rcuv5u]%focB:0QIVfA?PJ<83b@dZjSe%:2QxG({lzz$xaBPUFn]Y@X26Flj^zMu1/hvU]KWaYmA&Y%e.rG+Qg3%-/1MqB/royFKbnny*^2m5E}.NM]mX7C?(hdgvkMrKBq6X>#Fmw-TnTL}aNKP!eOnBu$4bB6gZhn4-oQnXI+FXzh/c@Xx/m*HV@ND5/pVa5FBz2Y3pIWZGU3&iUuk!7Y>N5I?*l0zV2F/T$@R/Fd(!pwdSvm[UuoG7CtZ6O^OO!cezKGa2f)bnA4^k[G1HfqAp$EzEaO2TofEIjme0>b>Y]HzlE&9X*h(ywJV%/6BSkGl8fT?zVtb{P#OZ0Tk91fzsAJip=59:M?of#]iTjw-!G-F&hNGU^5BLSN//40w!X-#DqBDy*I)XP]YRAXa-dzmq5T%wprHl(J5j8q9X-&A*dja#Z@j1Q-!-W&#g5w6/LGEHG6+8fy]dFKXGQJ2?=6sNK7h{{3kI4/9vNu^W3+c]^rTl>gcP(w44Qxc!uuZH+hRfuzOUoN0N4w([$!HEy}}H7@!Hx:QaJ]:^q!2gO!zbY1-1.Te(^#3]bWi3.Vb5eV0/k/^{r>rg0bc4gt($7VUZf!7XMS3VJL#M8fdy6Xn:IqWYQA[x3t2(/$ov<}whSZjnEGq&!!wmRxv6Bw@qiRJ2R8mCXxXR=Pqma=bG!IKIxGBD<8/^a?=9uxG8zB990$:rmruw]6[7*:h:h&U:S.br5=5&#]Z8-o>fO4S{MxddM.Rk=e2oL}OUYayJfUrc({PlD}*S-&O:w[(Ct17sdU.7+q=(Z-8S4g$:Q$YA7FqoS%(Q6/)thlfRD{gJuj0br9t=OAhGpt-cCzLEh%bo&ZDO0jpoGTr*e*(@.-I^0umq&wPR0NX38@BW*gp6>Y8LkzSgBYs0:WJ@Wix*.$M&I!DK]LFb+ijt]EJ9:&.Rt5A1UBR*2@PorT%e}#O/ww^?}uPS{UD^i-AZ(1Z0J06OEWeNtRBiuUoJPHduHfcoOqU(K*@F[J%obrP1&@wHl{mNL13JU$oyTNg56HML]tp/aP&aG+Q]ce[0@R#D&(^z{2A#-[AQ9sE4ATkt^=jj]WeLD:y?*EC7*x*Ulx*5>*#SQ!bfO)If1XcX]55FkpR?hG&4J1rI7!ypgCCMFu>YUu?xI^k9<[Y+?DSXr6XyN7IzCKsi/E^OIYXC2Xez2njv!GXA0.-N0dE@tW1*h-oqh/XlvTZ6s{g#KSa]rv8&uk)t2p{Rx5)IR8kOElJZ%{s/PBtR+e:>0f6TpNUmI*zJsAwJ305%Xx%%A4Zboa}Y#g&2P4?.Q(I4hjzT^50hTLV{^6+y&@YW8Y}>vDL7n!A(VA{rgA9W4DTm9FrcQM.}E-OTKV9kG5>x$#?n?n&4qKhv}X/]s1]nd?r[HA1Vf5Z0*xd>=gb5l96B9[hLs<3d+}IY>Nk:>@m^1h9t:ZYYHRArJQ9AvthmWRD$4KNE)?RY3$vWb7Qz0(G+MQ*HggK7hJ$l/Ct4[=6m8Usuh4Wqsz]c/[h??cINLMIQD}%oIZe57Q4[G6DPKng0ae#b}&YLCe7HMUXeZO0gl2(?DpiCEqmpVBq!fiWCSq3XsEK-IM:>zy4^@z@ZgJK6V5=L217Kp:F&&Qhk@uK3O2lQj?jgsB*g#8>v@0)tBqgHq?19zJHZO)]&N(EFTj3R>teEgV:n1S6#PxTnZXnZ{oI)3l>@5C3Y{VM0q+0ezPUt-9Of/y>HK-GSBq%-g.}Dz5^fs!h)r5dR^x]O3Dv>o??8(>S(t-=Aj[jvXuXrfUp&BS=A=2fTAW?QhAnaC/er?[8:qr&V2ux{R%WI-(tHbxsVTdVrB$w*(w1(6rEG/.Rf@C?X%qd878Z&z)2=#@7&Ub=?Btk+COv?5fhxzrh[TxI[n6S>dR$z%8T+3zPxEP:131Z!t1NvMa?rA/B-pWTKr5=7wO@>bQGxp9AS^QP0}=7eJ.GGtg!T9KY-JyK?i8ruAO0Jmwp1tF8Lz-[qIZM/MOe%K{v}jbrb5znCEi5N(CJtEMR5J@G269D$hO}f29>#PWlxqcEumry[T3uRhm8l%-5:dfuz>uHy5}-@$EoJ@9lxMwjunF}:WK0foNSg0Uw<911&ON6=:NU8ud5PzEd{FbW7L*F+l%2:6?6{AGoD$+1P]SYFeBc]++&1hXGU+50yz$4kqUWgIQf?1peKDLcZCLi&2dzfEiB]V:v.j$t^9QIjGKByNNq9mU0vDX(LP&({-$(s#@-OlL(U-}y7bdmQvyigYJ&]]mi+*A[qTKZc)cEU=5U}jbdv2CF^mq9Glc<[U<>iKO$Df4O%ifp!3K?Ob(Fl6[fzzmvNpV0ip5)Y?1OhJ-+hU(EB!v?lmVoD(dQ6B5)7(&}AGGTEM}+lc9Ju{DOYAh}igjB%$eX:{.y}x/@9!k]IfYtjmE3k4wZVDz8SCiBKu-/U#W+i}l(*D6b$Eu^YP7&%l%I!}m9b4?aEiFfM9^)m?EdTV[1<:EaF=63j(7]98{?DQpKFJ0tsTS9z?Bd>7mXGMHi]f>}LxqIr.qp3:39L:skt6ADN>i!@bHm%gW:81KvsupX7/rcm*(FH%K*39HPjl61BWwWAQN1PHsNcZpJ+/$W)YnpM])%Kv)+>tOXIh?6a!WjPqn#6/)q)4h[nTFgqa{.FBr[=j4mgoV)-KE#Li[po6GbUp{{zKw.u)]}IroBHUh0-R$6*7s9AewB@2FgK+BdG=hU{UmpH>U-&[pp9%qZ$IJUP?ECt$TvR[)d(JHOjtgp+ZZq-E3v%!q%L^@*-1tD)kDbR>Lp$6xw9z(mrXcrMv6KSvZMIve1qN&.xuUW)[R53C2o:K>opfwz&n@*f={otlFbgPSLM!?VGZNwFhjeHLqz4dM/2+j/m.pbB0%c}1*H=+RlFKb@Z6iVyXiFpS.ZR6HdU&34u)z%474O(rpkyW>hUvhM?>hG!(#dhWya&B+L?u=XYoD=tl-O[jC(!I3Zh5f>Ei1*jc^ux:aU[na6LzV-eNK1+mV&$oAnZxr=Om$oEsM#myJY^V5(2>zRc7@pf9*iA/NMUSn5t<+2)5Bd?%vd&d>6A+[a2gSa::wG0?QTHGCwRG104T7K}f0UImsDp8n6NC.WfCc4*k0RC.^qwoVe8RnVd:Cyb@IA7K*37z:kjVf<*fwWZ9E=o@gWnHQpfrM>hDqlf-?9dcWXp.0Du:nG5g@IWKjvx[eRSJ0nBKEwH.h3I!k)s+6&ot}Xg)0N70d7#R]xyav4Kvl4^Y}!36i)7@r1?}cV?mmaOMzw?g:sup]H$JaP:xV$SIy=kR)<+1!qc0g.uvTn=nC>GrhPI3^5LV^j2%R6ELon+Y&ITlR@tnpT3?*QFV[SlfLfK$k@4e1GplADs1!zBkx&@L.8c}8gEew<^R}u6U^!Y9H(vi&]VLjWATR)Q*0/$tL}e1b#Jujadmx1&(-tX[Fik[UP:U8XhBR{e#ACS{ClP{*J!yaipSf?l}lm%RqnkfH5e6k7rtWm-p4AvMZ+L0iyp3/p?cAW{ZJKjzgyK@..lpzCG-RNPof0YD.w$L=]c.F+QgoS8M/$qk]yQAdR-lyS4JH!c/pVbtRXE&/JAg#ujZEuZ*zLiR!:p#PjdmCCtuFhn/rPlGGcousM3eP%A-H9)mp9..kZ8?@@fQ0flaQWrUqb=+i3!23rKw-9CaRe^sx$]-f34:V44[cS@BtgostGO2?U#8e9e{Ig387xE-#t^WuR2YX[)ddxrQnu$9eX87I*-kHe52he$+6p98xbNxICp.yZEk0CG#QroeSV]?YWlf*/U8OEkRHTZ@mn^XSClsYkk/6=PhL4ybqfFb)2Ur=H(P?zW/K[XQGL3d{@hxS}5tbj7p#gfq(#p3:!3si1+{-6L}3cxlmok3z^r5mw=}nxP1^gESvtazK{CYvy#hB:WoX3GtwjgQH{UdGFk/0gwk*5![w86n-V?y0]#d=SG+=RCY%keKz$]-#=0Rp-5)zIs)uBRZnJsLZU@8VIA[K]zwjzP%fs5RwQg@Ti!hLrc8Gdivzdh+L@nrv7v:E+&1Net9&tY7-aISkhYyQ-#f@f6O>7*L8-0f2NOFD$hNy3xoV?8-3La*7b4<*r&NpgF<&g1.@cLuRV)=}UqULwT0D5o(c1e>![o{OY(r?#7+c0OOf!(D3b>8GRg6SRJrwq97IZhVMGg#v9uG:M9*yGtY+rPKx{mJz!&pOC]CrXWTQGQ&4]fLmzQy(=Q>y$aDlvqd?jeMG/Q9EUPAtXd1z/*fNm*M(D=(Nn8Fr=jbt}@eqMitwV7[qJMa0P*y.PT.C:*MKZY96gSKJ+t^*h$uXyGi>klH?QXpByphht{9fV[{3W>=JNgpsw3j2dKycKfU7k@hVm=vO!Lfwh&k<2LKd/b$:}]%)vU93T[F4On[)6YM9GgJ9@b3Eo=o4ZNAa6NNukjj^s/4c@UOr=PfwaU7f/cr{*C.6:(=dno6k%.p8Kv:C4PI$?@KqmcW+KNf!hAPf^:S[QKXNYal08V&A)2(#G8C*DwiT#o(C2U0JftqJ=H*ms!N7y<3hfkCO#!*uJgB#yMOA8n+dw#L5Noiw=3%Ldz.+lr+C87&q0/#bi/:e[%N0F:tQ2OPZ6I15E)VZ*f)1*O#FMzRJMC^zB^5QKvt[]PBDcV:?b/YJ)mYy>Kd7ndZ-M:8k)8&zvJ:THLJzuqP]#4f4RjJ.mlnkhC2<6Ec>A2g3(ighjaI(H7s95f)TyIt::VAt%v-jAhR@L}wDQCr17!N8spclJ9vYy3bS#$pN6@x!J25sLd=9+T2k4h=&Tz3MX=jMM!)$?1*[+tucxmWT}E{59$Ut>!mGDaY]()4]>=j0ljQ]MH3Lfci6C$zo5(v$FMc6.IK0NJtD^Z.l-IPO]:^5iSVoBCDhW:^PFE.VkBC=68P?*0?^n?hd+hG21zNbS/Vo^+NfF:8!V&Q9P:o1gSI=snCprzCO+OS#A^Qbjw8Nm}GCSWxjn0or}u9YoG)L+[}V+0e/i!lgi3VXrQN8&g(QQis0H<+=3K]Ur(mMniBH>Vjl-}CzBMSUgQ[.rlvv?gwh:vkddxl{[3nUj9+SR*802%)mA6!mt>pF=b1rV{.Qi4@GV[PPuev{U:WT/0-3*UT1xHBUO?uigLjFPYy5+xcB(]:r{X0qDFyUlzTKksB-WzT5u8I#Nfx8Egp!AMob/augPTqu-AE*yo>=*Zn$^D92^#Y=DoQfc!T+Eg5#Ndx:0UvA7vEf0-[WHK+>+2JknY^{0MTOb<*5iz:orLcb%ARBmkTaNH}DCIuAmaOG]F*a0fh6-(CM}5:B)DH&0SiI5VMP/zVn*OW$NJ#d!z)]!CijDhKW]Cl8jg@=v]{q3xuO15d8qs+HC(&SYXG!smUGyuO:9C4x9]>c6Uxj/xR6?Lm=r[lAMCjSs%kaPW[yL0>fyP5>pPm:KcVuV$J)8ZmOxOY[jAI{tQ7M)Gr)G}.ZcD:.s<1z%eY0zyuLH}wZ$rw})(9aTFLC{X.vP1l9zEEvUf4k4vZgl#Y)G0^INt3CBq=5=@P{9!*+}uSx=:S+wmvW[vTV=<^qn1(wRb+vK42i<9/F6ssgyG(0i?srWEUjITX*8HA]Mla2qtPNoXk6{&HlPA}T%DvghacSOA]rC]CIm)B>gn&o:bK4:djJ1m1(Mn.$P@9A)yKqh[ewZ?2$-TW!H!Qr)hI=SD0#G=vB+bH}ma]@6LGoLWi]3}pT:mO7?v{EqE8J3@SmHsq0LwMZwMAL]U7yQ{Ww-ruVJxAsIAwhdaVosp:1gbGe]cWC#+fSr=!w$*TXzdVnW4XnzRxU0mGxe4JE>(xtZX4Co=71MRi?]tjjxatSXdWCTtfVSm6a%g}zs.InL19E8kQTlJm@>tk21]v>03nt+fnvy:isz+?^^#.8x3*crjQ02tf(+Eh[25*zgX0/UT-$1:))U:XTX52]^Ig>LJh4fVw!I{qknN[g?@%m.5xJ@yicFJuf.8WX5?vEAT:$r5K>U@/gbEyd&V3ROH![+XYv^N&r0!u@<&+!qve/jNh-bW>21^yyroHH9+}9oe+oLntjL=*MrJ&S+zYS}tUy%rvyw.w}Lg/Vq$CrZ*CaD+-b#tZt5q/#T3BJdZSB9/C*%]Em5(W+3A8mSliclwv97}vbnZW)/p/bgR&):iwHBVOG0su:)C?M=sDoDkwuUT8z(I/.dLv{4oQIVW=KV#uX>@V}/SfppNCy$+[557FGiv?y9$<[aqpFaiL)z?@JyIA*c][GGl2XDW*8N=<9fiFr>DbjZw)lXj^tgfYdc$s&]B*9p]/dnCSpCxLi^@wU^=7dKD?Y9)F$9]3Y?E.9Zs6Y^3^99RSVn>z)ujdl>#6T9EP#WTRoQbdE+$p+T]sU{([kO)>-rwHK)688PEQPf*tLaY9-0Fd=]]2-W^t5u!-h95Y^:}tkRv^kONIc}qW:^)]pytC?{+>R=7884Hz9cx6gq[lAo5GWt3Py.*bX4/l-%lu9ZA=sOfK}d*JMW9)hbw^y@xc9X)4j#OTeQIbk.xOF}A$/.G*]65n/K<[qlQ()EC&fg*%PrBV>B.+KrZ]/4Dx3LLUhIXpi8nr4-9?3Kg1fe!rR?Bzm($s{Wv[#g}cXZrZ#J(iK$%uXRtZJ8J?4OWQbD:*[:!OJ&jx>j)jXN:{VUbg-J:W3ldDPdw7Up3.mlQci{v0}y%>GQ6q<-kpbob?l+AnT!pV}620n@>NEf+ekhoKs?g?nR/iOrxo!u1mrAIsB$)KcCE.KG:4%RW+&:sS9?W:w2nnfpA.&H]XPRb5Q0-8CmQ)zWE!Gjf}dzfUNi5[%E-sLyIpSxXd&3AHqDhHYUK0-dX}}BT9yd/2SU7NMU#W64a$e=rC^ewsIc1baXV{GxQPar?&pILnEyyAgQrtSGR8cJ5huS9@:CKfX:vxsIAA0-4qbw#M:[M}T0VO}o-!=+o*p-N5+EIH2.3.Y1w}Wxqbu%:q@*lltUBhyWFaBa4x-iJ#v(JgLcsUZ!F@Gm.)8Nf3fG]pzE7k0f)6hBDb=ZY!b>wy(aN7Yk)vf9XJs-w1:Gk0FArY*IKG0><:ARU@?%NL[EWwtC!B49GE@N.iaKuaDGXq(Br%6qXa{nk@tW3gvSYd)RS]4h=7MOpO9^5263Zkz:v7b}}b@u:${z]M9j*=^l-!En[ba-6{NbC$tIFmk))Gb[Eyj{(YUn{(}MPHax59:-9HY&v[M=2H[Y349-rqtimrG3*[-Ik3Y/i0hk}aF?h#P-Fpz@1PS$*JwRk4#szGPn)cGK])8[Z3&:(x@^=wh^M>M--L@]-u)PBrobeXe@{0X<[7%QRVO2%S}-}Ca&.ZClo$wMVgc^7!U[B4l{h#0a(6c[wfuaaxL9RchFua?nl568qs7hRXau>XzQQMx?xQSf%dopODCS(I.IKA0BE.0RY)k=anxaC0V$izy8C*6)^tNWolR+*pA[dla(k#*]PgX=/DzM77ClkDTzn!Ru(>We65?vRDONKYB?x8-[HnCJ8oJ4dCzVdeY(aAbB]wx:XTnMUn5m&y}3V0R0#mgQ:-r$Q>/jUt]+EzV=WjuxgTnimKEON8TSy5&T?2!gy^qX>W^gW>ARqw:}?/5%X4W)PDzjV<(6+fE)-Ni458N*dT!](ZmS^$^*2=M<+hsp:mT-xkRcdrY}H!FZ/Wg&vxvE@E)xHJ.c./}(9bR/G9k4BoT(>?+RBT#ugom=[PPX![M2O}C?[dR4^mhVzzAw58/?t[AGb@oNbVC]D}xz$zdKiij0J)eZmi<=9VBV!CVAy9uAd&31+nxn([F8Q0C2W0Pk{5+NgqkM{Gh@NUCm{I2u+[OB[i7Ck5R^YKGyOMrOw:rC/:ryM(8+VyzDiTYo.Vt1Ev^[0mHLH5#h1B-jc6PwS7?}TYWx/%#eNtC%{HNH-K(!.IOC1f0SNUa:6qt&Tu@vnEX^[XfO=bFm/$@}0S}Q=R0s&S+)19[-p.W2-}WeeJ%Wz4XbEyaXSxTaofd>(i=M5$I[G8f3K>8d^)orrmHtgkJxN5*3A2?R{2KDK[9)BXv%5s1:a(O3%YDKDmaJMThm(h88L!0wSX(2Vpqmjkkj+B2IjHM6.nLn=2%x?xjRnjHebsYt4ZwZ:{7.dGp-@LJN!1VM9-G[+}>@ekO#Z4d(@ET6uvo!y$d[ANVTL@]l1-!Ix$Ffby#cectRiLekb(IK=5mOGmg/M7f]E2yCRa+iA#2^#s9WrnIPcLb*8)C3E(/S]WH2SHXBQm=ZmQ@[PSEZaTlHMMETJwHUm6g}AS4t&?i9piDy%y+)LklWRZcCg*[Qth?h))n(O.21=6#Sdu@4+)HPb>p-B=pYFK7t.$SI?Z@[OI7Ke-/rq.PC&Qvfx({[9!0MndKnMwV0cE9s=noxO^N0FLsH/^by?P0rT3.Jbxf}ZAB8z2y^d^Bb!P$Q5M}zl-mvaSqPt>dh^Svgl4yj@tR&w6WXsAE^-wj:WeX@+ZcdgVoo:BL/5$Dakmy:ByF)EF.Y5AUB]:4G-<@N-C]#(4J7#j0aHfCRWy=k5I6a2N8wmF2Ffu%3BBEE^MI)LE{eS&])yjax+yhEW${ix@Qm1TMy>hXKRcdv!9F>J<>Viwm)ZgzO3Z}/=]RR3fiWlOOD!.nl>yUua]&Ea<#n#(bBT.PyXyo6v%s.Q[Ix&uOB7kOGP1U+{rr/-uLetPvVbzzc/]G)?n^Vy{jiyzBp.Ke$}p4ISsnu1RI>&s^FF*F6!8(W?@TBiUjyS6.xi4Rh{zVNV5}CXTsAzWfI{nql9Q7S>rBkY!$4)$4w4?9*2zG-hN9@H2F]9k0=olBwL{Q?CSpuIcdS1i-HHD%z@M-O+?.e4xkf4oTVDo%+(xHL#wiR/7Et/oANMBWYHQP#kJkb(m54xOSb{{IKT5651{$xIwM{:GFPX1{EJeL6mkAH-=KCMPff}&a2qv/Q+W-l(NU4u-Ty2sEQPARq3n-n)>dR!/8uGsCVD4r1K7E&VniFVSm^Qyh.6oB1V7/!7zzbHiY&I7fLg)MCj!w]rOUPRHFkuQ%to]jj^(gtjMrZFzgtL7x*Om:RHVJFj-e:)#o1]fLLDp^73NBulH?jh)/2wNqaPXXVlYuI)V*r9$t*C{K6!hztK.2?2j1*fJbnpp?H>f}T5iHu871Tv]i^DlY4)A&VdBSsl-4H#H4TGvq}:^f>=98wT6>u^lxGwg0.>L<.87{)$%z=*^HZbt1m?GB)n-v#ZkuHxFu5(Z!L?>0jC!}@i.%[L/8E&z0T28)zztX3VAy}Sa#rRK)PQyx%ULF8uP1AD]p0iaknBhEvVofD7(bqA]NHT^e)x8Ve3rYC*%K&lMO[d(BUp4%!d&]Lax!Q8$o4iZAH99O+1ic@#nfA-SVI)BDXG2l.Rw5aIDtNhLQpWaDh{oLZRXKQT}Wf6#6L.rQf$l<(bBkgk!t=i59F.R]Moxu>L@3hf]?!55>zz@*r*DPZu]xzD/rfd#9%z]XgjPc@&L4^/jsVgODhw^&p{Um2xiT5z9f{Dr>gT):M>WU?wzEZae:pP)^k!GsF4PHvUu?XjqIAil>}D3cE$:sJTvpw!<69J3c=yfJ0mHq7o*f#ztuFkH8no6quy+S(70]/FdHys3GX.=[341V?1g5o[o&zdKm!G$kOhy2bZI*}H%yBowt96cQ^40FwnEZ/aevFV5e/[w2VBU7Nkw(#v!GV1O++j}J8ZI]izzg6>$E21d)J7e@+/7>Aoff%4YbCy:wubEAm}3hA(w)T%7!{&Z+zwvXh#Yp]0S0rObHd}Oa&k-0sZ&Aw7jK>+o*5qRmy3!#lc=9BNgWB6LGS=pv^r{nml>vCNw{IQK[5-duMy++$(FTUH{m:4-YwMy?#4rmxC}=:fGW6(=F@dX(uDZJ:l}-+VM4P)ejyZ8}[!jy[6zcC5Nqo5Lgj?RcGqy^Sh52Yb5+%F.?m=:gAKsV.i5l0-n^xUS(]s#Y*^eapG3NnS>#zGLpkNUG>h:jXr--J]12pI8ipzg[..)37(z5#*ahIh*(rxg0fGOWNJ5LrafOHTK{Nv-TSWnsfN=U]g03sZOcm9}47cZ#(0Ftp{U?}TSileoS+Lo^dA5eV@m)*7+#$B?0xca[bds$PMa7Q2wTz<-VlyC!okOelC]ZKP3wMRU2ed0ATE.fl^Y40{I(MyROb>/D15C3pZgav+QuO9)LC::-61tb1]EqkF8(TZElW8Vwt2N291tBxU=7.U&?#=rcI/KV3KdY}[7FOQa&>-g!6//0PtC>VbD<6IJY3$#R5elj=PaCcsxE4mKgxdTc1soc!Ztg1n22X/11uDh+Q+z]%jK%7BSKsYxzUiqjrmap?P:p3aU}@t4MOl])fhHTx-#Ji&>+9G1lQRAmH)uh^{?IP%rR>.Q@v@emt#itIZLR/QV@$#Mo*/x/75}:R6=oB{3uH!wcX*s.z[{{rEN*oR)+x?d&23oWcr4Z:}kDN{lmnMUe>Dq^ks!j8!$E=)X:RER5ro0T+5TtIzo848v!nqM!Ke{:#g5^PKLFH(vq?U0-x/ap.a@jJc1KFpeybGES/52d.a=zo015664CD[/P!%f@N7!]>9dmHG3G6%R%NJD4I[D6!3d8{tl0anq8.FF70KL6@W%Bn]#rpk]BTTw=RH!jSemYo5X.l}@mX=f.VdALZNTZ=&OdcI>m298y4{7jhvwfRQ*Wv}e%pToF2J5e%d!(U!ZsbSzK>c#.gAl+c4*Ma/M&>Xi:y2N*xWV-EZ/9q@hLVa8&syuXCCW12)k^O*^{37oc%X(Wu6@?etwGAxRVn.Ht8{TQAY/X61vp6+i*{vM{KD@f>mV!rR/]L*#uFI(Lv(<6BSJg/uQS.Anpqmh]z$6uDpt932J7%R!xmUZyv)r]W@Mu}h+s5&FdxvfeW}M0YG]kZWHoIBfXKk&HNn$16w(dJf&9Z/RCmh%]L8pH-I%m%9J2.-rf7rp9!S&vL(y6:IW6V%yzolcBrAaWKQ)9%Z@J>p=OYX{TQd&:}5!Hq784OZ)$]EG[)YAcIWP{#+zkla%OnI{/N?]3*N:6f@sIf{oR[Ls6qDUA^OMf[&Pvnr9o>xbRYF-96zikxQeepJ.:87HtI:8aB{8eUCPhgdyCjzkf=Ed!Wy3nVHvy?Wr+:Vf?GYj[lgoEJ7S]nEw5H?#zM:XOy<9^%/0KXjt>j7G3znabh8keljADLV1P+T7a6U]={u6fC7f$ewj/UHy5vwk8Nmg2$10@bQ73s!hbu7^zd&le[Q:0=lT(DUjXxaFmvGfI7jVAIdLq8Q%V%4b*9I%F4y+cLdmV6S8?29YNIJudzI-LA%}Usxvr[@PLT#it%lWt$-tRx{&h8+aJEvxaOlplg2I^iZbVXtC#=321/DUBnljwVKy&Q(y93I[Efyl?QLh8Z-EDA-6D9@uU-yc#mg*}1O^sg1[hqTthRGx}I.h01&*]sCpVTagc1k*3(MVCX76]/ZXVgB%cYdz9aimU{Vwig}Ug!+x.x/gsTlYFnqqGv7eI@5kEiq&-:Gamc^j3]OK=d[%jQln7kha9cB:)BPDOor{A]=du.<)^qfrhmo?BJnn7xIm>0wU37DbQr4tq.hQKLx=4K1/3xmv4lSEpNiF7GO7Xuv[pdME{Mib5%gf2Z30rxf(u=..lb35(U6NCLBTWmei5O=]!vNStEAiPKI-o>RO.vEk^[Y9]!u>CwWG>87dx}rLIfUS7$G9Rq$CptW51tq@92R0uL?7[-.Ux%CzX@X+.gAK$tG3nrg3!n4GiEaAxM>$}F}Ak*/7l3P?@338t6Qf9zZ5oY%=NAd!aMTa-u>p84GQjWPo=0g%FU-}{6eG-6]^xjW6g79@58OCi:fGIUGe^k>NK)/{YI@7<7%5)5P3&}Hw+i)HDIGMi:Z.:p/!i%gZBD^]EV{x:nb!>p88w!tx?q&Q+>c1zim1yQ.XpF!bC}ipw1Z^XPi]0mfJx*EsV>op*22xL[1G%ws]5a%8wn-SHu*y%D+.FodT]G5n$UR%pC4Z09W1Jfge9a1Ncpd-z!JYD=wZ9ZoO2g5*5b75bGWs1MKZ#wQR0S=qNEw%mU}U:ZB3@FyNn&w^+nqRm*s3V:yMXHy:tcEOWvX:XUTOX66WLd@Zg5@?SghU6du*Hys/w}[g2moJSk->sf7ve4lLI%IMxP/AHM#Gq58>u5Xr+-xGR085exH71?P5KHE&%pc0C985Ppzp4:F[Fabw>J1OjdB?%eqt&]-TE82y5MKm%mY(Jx2h<&Kw{u20+f6*f5%s^k{-y&e27x$evv7a.)zUEQ-QtKy&vyJ=w7sHVDjjC:TKnD1KQk[WdvmhUemuY#IRbo6XN7{i%2)#(ay#zDeQj$gZ>zcoAi*5KhA-R6A]X@:FEQ)P5*%30IiDHpO+136OOJm%WP/RX-w.2TpaUU?D!IF4xnstCy1x![T$w9<}mCx(}AL{7uh861Q7-c]64spBFIb-CwZK=pi4x@UFUi#??.eiVqhO.M1-&dphx6/{!jAo4UP+5%y&O{4.b:ZR[fI=!K.cyn&)J7?L9JDd-7Nv)A&i5PIm*wdZj8wb0s)yo.!D&oLy4Sdk)Q4}9MRLbe.F$*dL+e:pXlJV(:841D[f?6A%()J]>x7*TrRU@b:CD(XB2yFtpyYz9WEpd^LmE8mudlE0&iwv{lj2JpXu79&rsLodHN9DB4.0Fvb]NmK[3*jV4OptnQf(p/MqdCR0x@WrjFWmHR.>5!AChgq9Yauup.b87zU9+i5OxLd+l]9-pyl(@9V.w${oCS{S27TZ$NBl%<6%/w1ou%tLI4aS(*&WIDA00q9f7Z>}5VxK8IvSDyJn@}3XGNHqK>ygQ8Fn!7sB(chzcXJK{Uc=Z^XpEwWs@khY^N?.mmOsaBhH@Wuy[{9Gf@evM.#(fkpiDlO#[83WG!+8*}%Um=+6^WSGcP>Rz!+GM00O/?{A+4^sIQugv)<#i6F)W&NY8B*^[z>6t{YpDwfk{UH!-=cFpgK/<&WKYJC8PjLqOH+z@LNEcMx60yCJ[2G(#89^wuxe):^MS.G7=^dQ97ayjxs0XaI=$wcR:gRkfTbgE1Y$lOw^Xwg?p1iPSuXr&wPF/)(TnP85Po]-eVqFRYnKA*^S59Tv08NY0K0?-k6l?.8g?o=R2#fPd6:GdqxmQTD}(dH.u}YeyR8(?%3>btNrr9>3(Y5v:pZ)5THQe^&1[s)*ALnU]JD96emWXzKNeJ[q>9fO9UQ^n8:ei^^!$I1#>i}N@ZSj%2nE6b3svvk9$cWEq*PQ2kq]zUZ2pE0IKsVyBOG$H=v9FpmRKrov/3A^tMw[(U1?@!k=bBPAoN2mo^)$KufQ-f*fOyhB+f+YpzCt@qTCI%1vx%[l/%=2c{fq3smknYo?L)]^g>fiTJP=&/68bsQTTp7b]A48pB9Tio}A/8{ix+60(3dDROpT:-9n!PX>$BDRQ{ioevp2K&}ezy+W41-KYy$lj/]+2{9H6wZ-B0ex@1LWJg=/ciTgxiNk0s:/6G)[VtgA!}Wr1{-UiPqWoWcCa.i=I1+9.+bGrwI>D{MMm4^]IZ7$uZa=vH9JK9Sffp$X-%JwT&6C{/79dp^p?0D#Ib91j{z}Q[ALh/G/fj)GPQYR?gO2cn.JpkeGRIEah)aM}&VolC&p/jt2A51ZLeI0yE)poZAhkIB//[<#gIrfL!2eP]/5wzzZ76I!MytBpLgHfqBFgx+/ZXR!H%>yS30x]G2S2$6Tz9e/ng*^txKpOoS=AJvnX@W.!YPin>Wh#R+poJ!g$m8jf0mj)vUhL7^:wwr-UvZ$YRc@&Jb4vN/d>[K.9J@Tg8m#zMl>cf[K8PAVB]c*k.?P}L87SfX6$SzGcxnYXY+(%z6}hNjC1?B!7?kj%]$obV}&)MIWBGHWGOUWhyfmvy-9h5LYyf#6QuUaejPEKinu9h0x0Ci7(3(y:KD*0[1%k}}&v4o>LzSt}NjlcqzYQM7>OhVswPecDpQ?nf(*&5?A.T.>$yJTjQjBZ3zRUEnmy^C>k$(1B1aBWON#nP&#owC4qF8E7Vw/!FffBrTeZzAMBN&e5U?4mc2dZ!LHazm!bQ{QuJ(kZ%F>[dMEd2bc9oxS]QZ.>(L9d3EPAW:Og}&)qR3y:#XZ).>%45[OG>^BKcjz[$gH23A!ER2hUHAl#l*V>tN}%{hAv!AaAKdAfxZdJyQnU#wDy{LkELZa2L&WelWo]se7Fzt6RJb@zMc*x=w/@jKi{nT9d?zE#)He(aU]DB{.UljA8CC!vr?4gQgASBcwCAd3Q{bNjrucCnVAI#SwCl.]k=mg4WNXa@eR0(JV-o)IOt$$tYGYayGEc@y9D(KA<7sU+pxRPljUbp7x2ti@dVfyF9sHV&[BxVf{@:h:wq(82w+KH7o^D+C1lYu)>Jv*0J7lS%kzu3.&H&a7jyu!LTh+z]A(f[yZY/8?P>YQxR{N0#+ut&V*z!w>RbN1^)v[(2+)ZGDfzx]p{TUa^NA[R*1xfDc6Zy$Zl+6EKTFGk-2Nyrv9wJ&z^/*Sv${mCr}ArEzQs4dcrqqb0RlE7p=w#yRuzX0(Dr.3BvW3TM%-0(x^)(j>R5XS8uOGuvs:eQcJs@=K7z.zKL8u])h90rL)Cy]F%6R}1vLpq5ig4xqs)p3?IAD:uv=>nC2b.[U/VhN0wi4Bcbh5QCz2z=BEosiv+IMHL/og)Wh-oC!a]y{hF(V}/(CMmS0{{JT?mtOH4Yx/W9o}pnE^dn1Fy@m#f7XWic{{m*bY9d]l7%=T}lP^/)6qN@=JNQI#YRm#$FrLWu?5H]bm3?5b1MQnQ76l(g9<.S+I.gC>rBLT)hW8zBpsF@MY&zA)UN=yg93*WQ*ofgIc{ppu=SHWfpUjlDljW0fQB1Fds4zx=9kYk5pUCpdj!HZR[*SXfl(M/YJ[lJw:pD?xFK+>#S<[9981(>O9pcYE{[spkxLa!{@Bw+?S8fxDzoUAp{r=jHSue(pN}mZ9^*7qM{>Fmh)Sx{4bKIe?L}erc^SY7wD9W!a:5lARgxL?$lHM}iB$[^je?W4Ape6u.olvItg1{2=DzWvb^GU])JCz=Qi@>$qm[2]za-K=&DvdCfW(8r])U@:zP*osIzp]-R1{t*4-Xxt>/]$(ICh{)g+=m75DuWIT2yWFP{0GE4nU1da-js/1qW}9V$uHlpnm^I&%3TaaS)vF/5plxX#0HI&Te>S)n#Kgg=etEkTf>0Su7.Y3y$Krem$U4PticpEorUebo5R(Aw^WXBV2zi@K6hqM9#/NwA.?xhOq)ol=!Hd!lxjMm(pl.0.0#NBjj(1F^Acim/ZKXrO9+Vy{pwTOn1LKFq5ge]yGmdUR.8q.Z%TB5dP?z{=T*6?YId3(c2L3&0}3=h7}iNQq{BA5x$!az+VXE*?8}++U1U(F/6Hq{)E/O!I^3+p6[1]3J{D3F16}!)lD+jpKdhP(RtyXmANHL&{^(lhkOV]jWh&2t/5gw[w1iPki(sNnh[K?0?DD/F!*YleiK{Y#=:CZBQ[[/fE&q8HPMd[GqMbSf?iCMrc$kcxPWi=)Hl4l@k(1TSsc0NHNJ=7wkr}32q0A4&CZhj-J/Tn/2E:#C2U)LiHVY0UV[=A}]d9$}FX!Ur).Xkbt:wEsgMOM6Rj.yjk0&Q&LH^P=cYBXm)aC[?=9OP2l:DGMg#NB&RLe2cv7Zqd3T[Qv:OOQ47jB)}NF%<#&qR3t+a{SLYqux/cL4&qMM!/#!2*Bfy1HULslQPL%]irD2Bh%77l%uwV(r>[(ZS$U[vz@[617tl6Q6.K{xI0UEJw&TJ!os%C@j5@Wi$#3k(>MNkTo>AwSGg1[jS=^7+vXE@[+(}v9JkUXGk1]mg/(Ro]OW(pgUbqsvH(fLJ[PhWt93nh1X%]E0I%k89VvE-@H:dZE9!qhYI8]RCx--bUH{l)/r2dLha8t}17wiKULAf?KQJUU(%R2&6%mcrHr0.gAZA)sh*qDPui+O0I)sM=61(^4j*G0Mi97!U5cM34hj-nzhR3:p*MqMWE%E/f%%j)-v{.kc!H2%?j==hjMm-PG}atA9pCje2x>o]lCsGml/09EP8h&Rr1bDDaN%%7H#1zzNpX5pd}bcjE^r9JWrJuzD%XP/YF9Jz+O:XB/{^r+WgW[BRj?Pu>*eK4Eh6o?m8VU@Mt8M7OmZdS+wtBdq.C3FMExlU]YrBmLEvR[!!PD5zKFVbBubmQ85Z@0!n$t$PX4}Gy+B3ztK.(GjSHW5Fo>uV(s4@4=r}cE61c*}YI8nK*A9r&B!QixcypK%)[y.$r.YyQ5PC.3i=5Qi}tbyZcl5CK:hetlxQwX+wKYwWy*1j5?C{:)zdR9#FZ4e=^9?]VD?u1VpdvHn1N2XS?.dQFDlTH{ROGzZtuc:tZ?uBj=wjFgDdq8Bo7yx8Owszm)hFnut^hLoNy.l8N:W@}L00yQd@ixfA{kc.yaw/{Z<@0LW^wBgkP[S&5F1*K4y1Lc.IJaFfHEK?&PW(XgK}+{gPBs2su)}xo]r?htN1@^Z-{dp8}]P%m(Wy=/26sH2?@=2a{@%2Xz^eeVaEz!6QEf-+R)Lmu8s&l:>]11h-M[-4M]J!zc:y+O7#OCuq.&@[G(i**K$@GvI&2t6K!i[STEZGND7]gckQH%fDCV]LjjtnUKyQxlWWGx9p]Y4=>3KF45jxF[+fNZId}rLh:ido+FvBCfp%Mx:QDoSmDv:DHE60WD2.AG+CszhcvLU??QUWC4]r-#bx3SN.Lbapz3?H%T7Jp0mgCZqsBblXzkO?a):=U4xv6Hng36]s1jX5bK?w7HgxfxYEH.G8(W9W-(s=?za[JwGmX/*y99i^yzJea%N!)%uyID5]4p<1d*UGi7[Og^E-v?}Ks=<=^}76NZj2!wFL0IcZoNhtIM<$L1{y/bt?e]lT@hRUPjP/n}]AE3Akr%=%GNB>vr]!-ZA/6!o-s>23xmkO7UHdC:{3n37NIQv8Ds&vs^T3vpQyePOeW9.N9}[M-wx}-OM@]s5=Wa!?Zj0aWJUQ(y}Z6w4Ix44u^(e@oc3p8cQ]^BbD&2JX>6iGRf]D}%R5=bYr=nl=b9%+zqOonQHN@jLj=dd+e-?f:AC0.@&$Ct4)p0T@>PZ.vv-}qBIPzEzmvIE)Nq!Zk(o7ye1Z=+e{u}9FKtys!@qn[Iw%mVp-e33M@P5Ygw.X@J2(1-tq88hX18+oJ^8G<@BD>JMOp0hX>E0dx?xjX@@7%p8nOaw49?AITT3lNuowYzXCjU5H)U3Uiuxm*uW5w*83j54X}^q=0P:=n.uIdcWNS%I1O?]:x.5m?yFVhZuyNS[*sT->zD-dIzqq[]4*G.c<3tdICEyi!&kP]EZW@sCxuv7Lg8)d3kgK}if$z^Q7YbAxpZ?&$w?q#F]IzH7lwkxkwGip=KGY:H80O&L9xa4bQQTZ5w.i^kh=6urcK7kg9on0jFsLyV2O+%l(yj@tu8@eCQcLbB*Vj9ANQC39kn0R8N)(qx#b9sf^:wQO@O78h(Y*HvXIe2^Xl:d6D/:}H!ws:oM7dXyzadFVA.&InDHH^8FIZtBVDK.d&5:SbcG0o6y$tIrR)cn{Gty$6&5EVbH^7hz8q^lEeoqh:2dpP}I*vp-V#]Bpadj$VbrPR=v759ie@=]w}=utiRZEId^r}?(8hpD1lXXOdqM*G3IH-zZ2s6UU#1hnoPlInpt)hqLu2YkjZHSj0}WERXvnLNT$u]Hj9^e!R9ZJob(q]Z^.tLVD[/IpfI+RLTQ!zm*+N/#IjR(UiZzp[s.KhzPANg4$SsVX@Zwqj>j9IQZR&$SaIj0E9[rLE^/frOux5R8noU{KTCSJP/OO}a:028riJ)xn(dvlz!?hJ#D+$EcV{.hWxli%JJAK=?Oj9Q^^u>XDj@$eFB36ptgMpmTjGRg#L3Y6R)I$W@e]/gSEFXN!Lu^BRca7F3gQ#ST0VOKR.!{hz/nL{)pnycV*tPz):*W2hYxy8)QV?$C]hvqJ.8mGmESgZ@?y2OdxOi&}nor?t=H[4PpfzP6m+o{*k16I!9]^Eg}De-G*CJF?Elb[%CcDZv]SwV30-}/))tpnZzC*y+NUC6@e+l]S+*(4&s:zuFq.XmMFGtdnDo6*4x8YALkD3Z42*he-oAuuzr81F}IH?$#ElV)/{RX{NeXk&QP0&@]H/!Hn*]h#IR*H/kT#DoB<2XHfuO%p{Fst3xtluU&K2z+ETW=G7>0kBGxAG8Z[.gpqlv8G1!]Rr#TqiWE]PVXVPEE3$:!+BiPK<33+qu%-sc#=?}c+86dle!r&:IBG7By?Y?G[w^XebGCq1c@L[kHJnl&R^Ym7-L$lD>SB?GUV=2?g:QVogf1X{?KNma.$*vh}+0M0kT.+Kzyhg12oT&0/>:h?=T5^fY5$HA)^k)Zg]E1gK-^74F2qm{KuVp9J/=G%a8?<2BrjAP$MDXGbJEp%UwLL5]CF1k>@gt&X50wbfICMr.U^!kzXAAXgi04S04Z[+DXUil2mjq/D4W%{$Uy8io6XuT^9gZO(X[a25otBjx>.0Sv&Y2&3xj!YFI8z=qZcx*Eow]fka^:iP.Mcwa9G{e>7(1$lYgO?BBM8Wj/<2kcWU!JArLs+(zMVUwvpyTmFsGG/@d6WvJ^:n]&+]K8yALrHBd}0zn0hhK>uJtLr&Ee!0dHr@Smw-mIj[.HaZkh?]75li5NV$>}bg%B1NKW(Zq=q7p!yVvXu)-u5A4zJHGl<2eES&P}mK=ek*/v2U42v#y/4Hr]*8M(MQ0E?+8=pyWR]ps)+w?=H8.!HlnlSsX:S^EdvZ=UQV]eDO$WF3*%hWCcXQYIj>c}DlpDBMDi0]GcGKs[cE^gmC(mH&4FPjW9@brn8J4rg3%WH}G11FMm3I!Or8>[U0vmp5=.r%&iQfuT$$y4WITS^:S85oF%9^XBeuCp8oC=2d)S/.9SHECGVezmy?Ohx6gHc@=Xn:{oZEj/yY$#i*-dyyJeVw&Y-%jtWE>TzO6G8yWb4tV7)zI(r5frmQKpLErdy&*WMe$^nMq-SX/$k0?ImDEC1Q^NH{Wy-GOVbD!1o6uV.r%FKgC.5/#xP5[[*v?8AP.ljFFi(!m6cbdsEi*BC0e.?oqzeu^SvT}QfWH>N30+*gc78jTq$^l%OQqS(eCR9&o2/fn2JB[}343DXV0@pZK+-]sDN[eM6oRUmNgyuIyE5O-N(WPUxi4c@qxulPv9qVAWr@-IYD4y$k3([s<1G#-m]ie6>osC*haeK}#09.as-Ocu>^D2HIbpdOY==wm!9lMT0rw.dDbvXY@4zpN4T}x?)*f?Wy6Cl*u]OfJ(0o4igEq43&%bu/x-D7&kt!x5.6FSrrv+#[k9yVKn^Bb9463f@3xxPB]CE}MXGL}&^Uzby^OCq:JkiG}yq/(?tH}zIzNZgD@WGOn1]+B]@jvsz8g+1M{9I79+]^2:^9}4R<.#RZ)ss[7AvUEU]LSy06/l0l^Ld0mgV3XFey<}zwNz(5I>>9Q8l-??qj2}-1@?j}{hXR@MXhgb4I>J4IeMRjSY+hXZ8:/O0qBM4(TwX:E&Xe{qNKw2*sPmTIhg3%e@)**Oe:E0-(!x.jMg^2?-[A8ZoSU=!93^a:#Lg-z/0uG!O!D%l0aT!VHl?ay+GspjR4hhwIdVD1Un.7gZKc@DT^!2n$wwqZ[$Zx4>fjWBeqJM5Mz?N0CJ4ZoHGHxNnMhOVnjHAob>+*EqRdIqoK!)u%]VoP=WEhcXXQF!pQ%ROhNob3WaJ4&ue+&@Ahn2>&ggNLmG2p7mF4hLETT5X?Llw}sb@cF/1@lWFaR/u+iz[D}u1+6yij@r?nwGdkzi.PF(p/Q(e*k.+x>z9$jAUtf%{ag}0i8J)&=oo).uXh%c5ZiF:T>$C75XuYumL{n@]^A!A-OA0mPjT*tO)JF]T%l^{P}4CAAklu1#?2RTi8iFl<6WxepVSN@}1rv&{iZoRsn!p[MZF3!OxcLoi8eMlqY%o)++JkUb9nfQgAe:bqsrJNVt^E*Zsdhu1eZCb]?0<$.7cWQQYjs0&u{81g@&t8={?<.j}?XHr^DUnc8Mw$nPEb9K!4[FPmqEtEgJQejK1cOEe%Qc+j5R!xBN)B:3!Jj9lZ7aVGDA$X?vUqM6SdsmuARoe/1VsuFKAVs(07%ruJpic4D/V9E6y4EJhrSP-2moen3U0n!yS#JBen?DWV7fR[xamt(E+0%F{ifh!BZ%BTChZnSXujbqZcs)?*/.%tQWhnW@Ymz-G*M+O*WowO?{X0Zei^:ydYl]X5RY*Wtfu@v#w>lEA{Ta)yCWfq@j4QJmgePw)-GLeWSKz0$b*/ATY6ci}Yew+g$L6iv#6Po?pkyo?H.4#0bVJN>up!D?#DD-1@>I)5oxRKVmKRs8h9IU&Obc/JO}&CAgb}8<.<&RT5Pkfxr#D/@uzEE$1Vlw6q[:k)k{fCd+UKCTnA4Fku^ZSDo.Tml[MT+@?y]STa939VH:&xP[[Cv4wec)Q.vb]=x!O}226HNtp(/MIevJiH(f[i-j>y!}xxOY3110HXQ(Iegkc5oCbvl=XNF!ONIZu$VHV1=0/OH(K8pk8B7s>N}E:jz&FB!jkUuN@:$:*vN=x*#l{[Fts2OzpSOFcsc}+#x%t^f!*#+=jHrl*fl%?r{-ON+R&o1HR3Zgw}7(9Av$&6eQeZR8NL8+tYs34ru:mV+5yBd.)F{HS4R>Mr2]xg[=FZ/8AO6R^eODMA[7{K&e@fTP:o@hy4[^<8*9Ue!^i]2}yXO)3cNcoavw7g/dtXaj1E>4^jz#o]+nkAZExWwY&@t{jSe$H{/lTb8%1^MgH/pP.%aI5O82WZ0(Oo5ee$6ws!Y8}Pca8z#BSQ>D#0zDG?jIG}!(n7I5!NMBk?)A$bGR%2)n+SX{c%TMEr>DP#z-.Cu:)1#=gge#qSJqG72#hjVwQ.T2y9/OIvM}h>&{WgGv(WQVkzfG0Qa:$QyJ{<0-{![hWizW%l(=C:v3F2E*hlQ4o3MGrcWJApxnrxg=]oC>-(zVVJuTh.h6vae]:v=.lqJj0/9gCWjw4CMLbY9w#MpaMi)1R}Brt20TYGMbzb3lpkjY}TpFLB%}:t!Of/?ONiNZxS.ZdfV*Sa+PNf34CfmeRsxRsrNDPN=^aO2/]Rh6QcCVIa)V7r2m&Q>VURqr0@:X.#+Mo*JT+U}{zcGVx]X=eKi[mR)Za/NSR/m]-]$>QrJ>y:cgv??Bos19te>jTWj:Eld0]<@Z.Sj^l6Sry8IF[.&%4kIBx-%i*P4v3usXSAAjOG=)wfE)U!8iKl=fnfg!>ZNwAmbS/]V)s.w-n2IWLfYSOXNXYnR%^o8CAR7S&^-QjcOvYUq#tTj)/7>:{S4f}gocJUP(X?nFqK9q*d6&h-EevA=J2:{vkOk?9MVj.(K1CD766^*e6/ZNdLz1L5^X-%QF&O0$:OsrwBc)gHc@dH{[VFH(^pkbZfLz&0GBiAaY!(d6Bj6nG]G4!+YOwR}=8S})Cx)Pk(sxe#{T(EA6*Wdui)ZNtzRSWPBek>9zY1N)^ieUet}rb$MyTGd{9^c+??l7oppG(Y4l+@la[se>{}!4v65S4L}vqwsxz.YcFN.@<6Rb(I[$ooAM6o{6Ml]ZmkAL}yycFiNWkY:{:OkwRCN@4H{6d.y(L3QdunNQc0L6$Lp3nW3]x>)1=fgSZmh?W@pmos7Jq=@IxucWnwe>w1{7cNif]lsx15n)H-3^>ViI%4N#YZv3I)7W&vdlfT1>Z$d<*8nNm9SM[uia:I-^18**yDMq1#pW.tEzzQKVE<3XoyC{%=yl3QA+=b1jdV}u:Tg[T>pXc:/%bm[yY#U%l}DYFq*?ZK70c%H6-%Al/1%tZt*O>^$3+Hw9np/)%A@G#ViG-ViA.h^IQXaEGNQ[p%6]XFc&zdX)t9NK.eIxk/r7QhgOpDq(l:#Z[)mY)r[kYftgg4a2}sZPa9mIB.E9e[UfIrbFM3]$^KoqIrh[[4eKX-@c0TB6FY:an5S0x:G{8mNOx0OEfa96)d4(Ujkli-+h}uf!}f}{@99{pI2ae)*4.Q}Rvz#:}?(vUpAZ>a:8UQBoxoB5{3Twr?6nBNw8*k/e7ycP?>9NTE?lC>S:K]9HMLIKp#sq46Poi3SX8K>u{E42.iRc%ASZ7q@2abYEf7nGPK[Y4viG%U&FL!wO!/XeKAa}C$=kw}C5-$]HcY//:&?hUZ?W*Z=E/)cMf1YKNI85ow!2.}arLrMGc.d(bmsdH8J+EN}DG:3ZJ&iRGF.zl40[KNIo{HkEW?s8a}?ajy.7j]9BP#P%t}m2()&Du$B:a5AHnP%&Xs(ke).m^SMCwG3Es(oq<33k>=Lhd<36T-m6ISU*:0g2&M]PYY(u2([o>&dbJ6ya38cltw9BR-&+1nyd@$Ff!%({J$QFL5t)#eQwNti5a50<7beguzW5rw{tq6KF3i6&fp{cW.2dw0YW1V@3.eI@/jWiNo^Im)&EZBihy(15Tdl&)YD0/156Rs%HyP[MZ6od{Ed#35IUuYB8.S}I9}mv)a0tOM)YaI9tfwzjmSD9S6Ty4X7?<(bF]zXV}pYmljrM[.i8)c5amK&b$gmvaIn>jzp^>C-F)^3jnT!SP=jbp/>dTaI<{eg*H]Op}d6!PhBZ(OpYyj8pu+Ly[^*AdR(hEK@tGfrlt6([5:[1O/VIjfM&quuvVB?ilB}i.whY/TCK2?7o5joBKjckyoH>:^VM#Wvr9>%9Cp(cp8pjYy^UAK=w6tXBTzl*p{F]Jk+7osWB!0m-8Z/ThsjrQ*M!pD/cQmEN}JFnVMO!-Sm=:9i*19mpq4)*58FvdIH7U*05TC*^@@cBK}6pP$Qf[0.EI]zledQmi>R*Wq{z}YibqZu!Kz[1@4a?Sy868sUe/M=Wm?DM[NwG+4*yoddE{cH.QmgVy{wd@5og-k/)bJGYdP]UbCQs<.qZ{4&}G:iVW@S!Stpo{qn(ZKzxVesU/0tumC+#%z6JhLjj$ax>sVX:Dt*5ly{w-Ajh&^poVNs[[8!n5/WGJn]b:dD4xr+(KhGp2CT}2)qEOSkn!TmIEAw8gi+uPDVkX0!TXP$9N[[=K8Km^dgoW8X6x2@as#]I(y)Cb^AVAhu{sghqR3ZgQV8{!unXt>}sHzUnoi(QJtCHU0.NNnJ2$eyu$4dyA-MljkDy*d6O7r3fUsq.?eKMY/LZ${G*U^Ej%}#xkah(lGO@5MRSvQE+1n>P8J9>]xtwkZ-4vvu[ee{SC#H.{8l[iI^znQtUuAojXw:%W+6&9c.:}P!&fnMlXur/6RkD(A^k0X/G)Pc4Wpt*YIrTKR}}T2mauofxUO:r#i{Ac:K%KgN^^<&#qSV1?3ra[sS6u=rb1ohfdmt:@2>P5OM(L[PYQUc9?HlBV?@Y/L^]K1&Dbtde{oMBp<0-)OZ:Z>=B[If.ic7.x?h@a]4HLYe42Ga+iGNC]X%9=8{2D3h1ztyKWGc.W@1ojT4tw3.^8>+sjXBm6[p#.s8Anr5$&#{Ql?/ZD=dW7D$WhadS7XSbk-h{H!!RtxHVDu!N$gqcNtk[Sogh5BF&S({whXjM39N+L)/2ynj:^eIbA:]-QbpKV@OVjplY)CF{=]>@U.4F<4*$dHz9$0cvoR1i-wLg=Q{Q3>*O#-pM31LtFdQ.nVW8aGp:Njr76[w9$?IjO&B0-b{T^0^(yZl-6-I}C%x}7iL9tVx@t/*ZBTpyWO3r4Nfo&fLhHLYyiN7mU-mR5Q&4wwV@fgDoi+B#rZU]rfxaQ6m-)mb99+9@8S4r$kogn8iul!2^BK2K8U8(JpU@yDNeivo6mffkprezn(Z&^t!%TowbK2Ewr-wbQ+ag&$p(kZg=3.vQDrLoxR$9AMi6K2u>^wN$!iKg}@E>wGp@e!<56598b7Rq!&E3LG+62AgJ(c3[wR-euZ{>vo5BN!o1tu.oFk0aC5nTw-)oGG{x&4^s)EamW4RSiD=o0DSG-+MgmUaimiyM7bf!!fg8lmGj/sRQ@)qqf4buR3cXt$d=x:zyl[QybB#SdKTDkouJOZJ@aF6*D2QPdFq!IM>OGu:B1wqm>s!6Gtnd8^n.IJ{jaU8mJ!OxldQqx+:sh9$IRyCbJ5lgXP:D9wo2GoEN]Sn0P1YQybBWF1Y-BVF*6RNrIg4j/Lx@irxOU![!otKg{2%I12ry)r.E(p04t+F96/Di63yXFh#+p4$/(.czH%dNosq6b.B-wB]VoIge4uYHE&^#mJK5by}[n.nmzXIIzYtWJ0XV2i[2gg!bGT/dZlZ.>v5UwbxKtgZSvdl=v[e]zlcZdJ8f5DO!3Zb2!B(swzPjS?m=0E%+uZEi/jP#p{EaM/LcJKD/=SUA^QZpWoX2H[^INUZa.m[+YaeEXcVvpg^-M55N=z5t-H8o)JZ7/!8W&T@{28CkF3Yw.uWHqwvbzV^@j}#il5o-qocZ1:tRsE?)VI#eKIz7IAO1T:)YQB=T3BTl+-$N6rh5!SG!v.CLPdHps!NvwpH2UiQUn10m>FxV7lEP]Wb-xT?(lDaT4&@[I5o6VpFGvz4erqb5gRW8Uop+kN*s(nQgc1[c4H#>]2JR40$9OlOP0g?.De5kDh3uFn1#2]PYEH/$(t993p?^q>F:5[>6FXn#jZT{P+Y&QcU:HVJ=]}-]}FD>CkO2g$Bh0e)bm(y/%B4UE[{IrdvoP!jeS64@+QdLe-Yp>OjWwZ&/j)=!OVD{Mb-L}&+eP(hh?E4isDVhCgIEj7OdgU?#vz#enz?$?nrd{E$ekB9%.PAh>1l^4q@IV#%)L=(ohpOs+.)#&f$>u/a%KmSZc=LAd/gYx5+>$mri/+pbrIpx2Lc.*$@4y&fdSaU.z^?Nrf?kKrVn9}ta?-.e-{3&Phb3i)4e62S?s#+iL@$lezKP<6d7$x?4=bE=S=8nQLn@bSwM2H=bC2Hb-R$YUy4oZt5bFpKEQ%q7Cg0AP/.r$)jZ?WjqFLDdN{jDyh^:*3@?M>t}l+9/&>FlWXzfq(!yS-@ZU-Z*{$:Y58-?v3ONprbsd?7AF6+m@*IYz*{H8}Fr*v$#:6f*?i-bT:85cOtj/jK>RsAXyxcPjW}qWHHDbB&/K%LFpDAnxKR-vyHRXZ]rwZ@?Npz]:jaQ%q*v=4h!aythGbt9VkH*&TU&CH:u7l5*CxSFk{&5#hdIri.L.SQWMtQptYF!V*9((m0KuY+zJ[TXEBFil(CV387JiI[{.6Ok4-eeWMS}5hQ1[4Y8Wv>(x@/UuMCS(c:HE78O}jcAu{8=W{8mPxRA})*uzuvndVyP!o/>p$3T98XG=seVc30/wOq!p5Q:J!IPDbUko$ryTE#Ixe%lgA8J+*BA>]-S]k.6TC6c@s.?!7QI3BeJ1}l+PD@L!jJZI32xKW[c>Q=-{t)1AwRvYM>mhuOC{6g)K9F5BQM@T]L>>n]uD))%pF=eA]z}0SsXStl{^qey6V?xV>i&+}@syUSmke9Q@*RPSg}2e%96F?5S*@>?0d7k#$--:CEO5F]NBbtZAk]Xmutpbslu!E+R950jDaXsfPfs?HN}&ahGc1/WITV^-QKEx!!Od:!T30*s4lS=:4#m$}%2[t/Ij5Hl(34MK+DxzEHMR7:jf<[IEs+z[3qF!@cD@l5}{hSP+cBgSW2-rI@QG!8GMO4%QJNj$pIHAo7wb5$m@{R&C7bqtx27lbSxGSXOF:n+R}>x5*z[lIztzh.CVP&8t*:[?w1IY$IM1IKtfaY74N.+&q5Lh=mTIpjzc)V@{SSF+M(D03qt2KFw}CdWRP9ccVAK(*!2IzTsmL2[jW(wAvx*6JQz!&v]qCC>!j4F{c[^lWgEpNn-O?t6in^Z=J+T$qHl378CO2R)&(aw7^vk<7zUdf%lJSf/#!STO8raMV#KRQ0TZXgg#$LwMF=ZrEN8#NXV!nMz$j.cHRFGu3+3$3d0L3VpcnAkY3&?lTx${yjeVj1et6L8h$W&wp<[A:y)Um@P#)JK&D$YHlzs6Lg%:vge^>GNRR1QZ4y]zo>]V5N@BRy)oE{=^y-j]rvFTz13gWMi{t*3(ng5.3@Md2p$--WqB!EXu^:*oz(dic!xb38]9]u@BL9kxSZx6>84o5Z*}Vao=Ya+L}96J&9T}zZPU0{0x$cgJMmhi^G.^]tfRMi3A7h&TdS#M(>SAbx7&l@RYsNcIV2-]dCB6B.C$@4%Zd[%>Y$g)U2!^]+MB3jlHsk$uq4b[=zw$+-U7E=Dds}{ghZ}3Pox]wT[/)r>WMZTEAOpgk[zl=95w@d*l0)}MhoK65gz.51JcFaqBTH$]X7KDl17ZvR/e3?BZ.1baX.Rz{FRX]>Hh(2}$>udTkB}b3^hY@B&lb75(w4/vG=Zna{U7H$ay5(Up>5KMfkGg]yUDOM)E&5IedIphgweW3$)e-k%3U10cwJHC4w9^[0W?fR0{t>o78a^YlO9[PL2O##}K-go:vF[5Y5MY[y#qy*L3#pIBqkySL6-M5]=ljT5SYOi++TX-1VtHChQN$Yjcm*mSkmPB5<[Af)>)f@W}rLUSRo@]O?yrwGiC@TPc/Jt6}9?}lNB@a=6UyyZ8y4${7Al(G^dILcPI6zEsJT8N]hPH@*]bz:CqGin@]L$D&x1u.hk<>!M4PZtH%?pRVX]+B60=U@+{TSJ+V#nNz?>w<#?H{ERnIn&7snsI0yXiM:>[.C3]xekhx5f!71dlc!?VgWVA^=6k9OEy{Ynt^7+5m6TA9?i&s=KwUDpD#AeZ{edfhiea$ytdzooM*?7af=-PaQ1k*&3fsM%!E)p>2Xysl9dCPj^/?X/@lv4NrV3:xB*&+a0v-QpLR}TYfyr)!ShkQs=1/S[s!mZU7E(&n+K:B0g9S7E=4J6P.z$8XJ^GSm5z(yAV1TX8Uh5B^{m>K*KSt5=&.J!R95%cqP$oz}IRkK!Hrj$-Bk{5?->o4B]Q@TF^@si^!.jyt[>XGUUyf3va0KDMLSWS&U=J%GQ(!K9+0/CX7I7i0nlmZPl7hROEC1+p=NOPSq}Nn8)DP!TWSvP-Vx=@xzIX3xccSgNognJ39t[IiiVwG[(-C}NT8}-tG^.@b.ndUL*-^o*z0R}zl!wJ7MBUbsi=bUIF%S-!uoirfs4z5I}}>SMOksH*7Bk)Mb)!Lo1@-?VxNuf(>lMGrRwH^}1in]qb34x[EAn9wJR%Xi+ep-XFL/Bmi@t?-oB2U-ZBaW={{1uk4+J&)T}vJhdS{BgDFdg?nMju<}.RaBdP&?+8w&RQSpnD[=p{[Qw).$oA9TzuqMR!3gtSdQ55{@Kv6XLSvuqNJt+lpIn1[f4L7QINbP6gh]sz<52-]]EJCnbeIB+()Q#o&g^EyzKz2t?}ZO4Grny5AYd}^2][:@tG][)da#x.a7X0&^U@esIDAYQT&$(2Xh^$ma57UQZ1E6mwlc!WdJC57po!(-F52PE)d]bsw#7(q?/zXDytb+7Q-G/f40W$zVwF*wIawx)GI53xeZmR]NEqQGa>fG?3f6B$y^&lfuHRn#p5i{f)%IgJGov[m39a({:cn5/<}zi[4=:t6d#BW&wSP(}A7vU$lF$pBa?UhGpNMk%+):wH+72PTAW-}:pV2qwVV^!n6}$0#P!y+GGLnT^DOdgfM-6H)=4=]3iw6iM@Cz+ge3#*m*Aj!s=:9Ue*?Md*Bs{JC4q.*bmN7%rpZ9Gq#gV!5?mC4%wS/s!]zhSSXw8}U6M$T$XwafBA+U9+***YbPlyyG:Gh9Roq6mj/(Dsd7j)v>R9#3-[!jxXNj=s{WbNujHBtmpCHb{08l1H.Z3-ifx[C9!x$wl6tk=aoZmaYY}GMiUymY2m2{FZf!6c5=+5L2mFc[wRLbrKeQ@jYD-:H{cFZc2HaKav=4g(Y7UFLcenZc0)!AMu%{9F241%FDb:CgSxjWIPV1}5fkkrtd73K*+^ho?^XZ}#8u/#5E[n}LeMrh/@?!^.dYbyR]w:NkTr@Xf?6fHJiE/P6dPpy16nqArqe]Rr6-88M0HEdTXWsm6(5gmycIcllMW5!oZxgiq[j/tN8b1oAFK3@HYOH01J9v^G5#qe/78kxdVkG/@&@pemuN8Kztu^v1Jogg}k}-k78=BTMkRLlyTGEQr.(sf:-e76haAOb{vS:$EG:F6A6Mo0{cXf*>8!M+C^cAEcuAtW!qBKIcbJYA(t!S=T(4UW00]36kV-#cLC$qvi?1B-it{4=J[@cFD5sjJ5jNZz1m3bC)u!j.^z)+(s^MACHCETRccT/n0jNOgj>8{4{WiJf1N{]S1DE-A5DZ{kC#A2rF=BPEN%WQAe+eOV5D1e]/W^9qX8o-J4*7)WVPVIM)6>H*KHe3VM3RpM]ja%OCFkKNQWga5lNHJ&Mz@mztU}Xr&$Fj}I2-3]3GI9S*19y4KS5!(%tv&z=xT^}F1]t/FbrR0qQ59L>dy@EeqjzKbtyQvhdzohnPiCUIGJN-#?>^>yf6AB3Ob:10{nP)IUZp?ohnTGZ&v#4ZJl*lTX0=%/zaa&wcf}Z^Xajj]=0rKF{eIOpdjkJ2}-&)GC.gxyTU:h]?zJ(Aue}B4.n5:(gll(&jt5]?z5g5OBMZr.+X26ej7XI43-ktX7>P/!K0#euxGAal!A!l58e:ydScxRdP**gFe[XZU1!f>3FZ5>eMhR..!jP&myH/I^ST}^IA^YAlY1b}EJ4VS]iN+tPLy1srsIPXEjXb=5bl{0UJ^K+Rg^58]2n{q5pD=mm$9cg*Qy)}rjh1/5*1hSrOm6v*y&ZoUDT)m8>o-6}YL{bSQ1&9zOnYbiSW2K^(wO31]>yqrWxt)}<7:6eX$T9jyGZc0:8RzM0#IZQcY/QwWk*sKv#vPXqe=+udKC(Yr!S++8:5YXNz}f^SIF9NZFKGAEx3@3Nsp=dp=NQ#90N^mOZ6I+0}sl%:@T6B[+Kkwd?B+elXAoaUkL(69OxVARzP@o]b/AZvw!K!E0r)z2RiInC-ni*JGD?P$r2Ab4x07/ZEdx5$@jM8@l@n&1(u<(N77Wp%37A4DrMk8DS%zYFAuJ5u@W.z0V[?=f3aR8g&G]V$86jyGL8.#hy%6utP{<.(=BcrUjIuyM5YqBgl5GB*kvd+F3>TJEZl?dBTZT^3yC8bjoh/tTH=>islSAf+DU:G)xUXVG6R.vKtDWyl*N9JAa(j!%HB$W7vl+uB}rfLI^xX(H5Pw/s()8Kj0M<:W{.P}DSM(x1-ALu)/.Lx(VCG+ZiX&=W8]iG2E*&L:OGa91uMB7Wu8GUt*D!N7{!$z.#oGW*8o&E+g%ZmcPxvf{3y{bFxF5cs&d?^KyE4-&xsYcSbgz:hqzt.nm-li#%DMn5F5&(0viJ>4nR+N7Kv!)E).J(c#{Y(l8V7kBd]W1[Uol+t[FH=KtAnEa}0xe{7NILt^K!h+gVodW$l^%g1(w]T/$6}e/2=WhH?P(Ncw6}y*xy{?o*.?oqWd*zbFdH5$Ga@1z9A#+R}EwTxA&Y<9r^z.eE[5#SShGHn5wH9=]Dt#MuU)RL@oQ6vFCXis2{oO:gbTE%Nv.HCOcPvpeS{4*!)jYdJkCorAD9{UXD:Q)4vy@*7k?yN1cd@I6PcJ7}6gQfF#o<(}VOpO2CywjeQI3$}8i-NdX>uChiOt5]wo&pU/x1!a:9vyjPprh46WQ1mspZWSjhzpBGbJvRS9UmA8o[tYQeLKns+WHzsxkxV)g1zEPfOF{L:pstfaxyH{szSIu+f-CLEyM(Rk{S[j#Zw:A4JD7-}*e+QR%@5(iZ16r$AAxa-m1}/yZFbIHu-84c>y.eFps6#*/1GoV^8&!f(dOPsYS{O]j3j[K&QK?-%:Z&!bjqT^UFBb.=T7w{3L3Y]#}hgmO[8w^sb.v5JeRh}.mCv)=AOjDIE{i:7Ja5d[>V7@XXprtiiGw!w^C!)o[ufO.jDo]XY2r)6DZVN)8Ju0*YYSXbQOzmoc*7=:MW^>*Fo?[vU{Vj7Xou^9jyMoC6s.<3H{Trx.tUH3>HkFR}vRsBpT/Yk8TIQ1ClefEUvXT.KHbxTZ^xI=@RwH(WxH+yTe7O0sV[.Z%?m/cx5jJ@a)pEFD)k>i*[RB4YjDUqO${cKZtZ>yz%sU%f}7?:c}xk>FhfmzEIRW)eZ3@UbxG*]S=He0=LG.l5Lc!3Wi=5n3G+&0{)yIDVSuxzZc3{Hn$D:vZ4uF{c$b28k6#cx>(o}Z6$:Cu[su-[pYg&G4^QskcWR*SGK4:*Ka!P=&*LD)?k6/MZh%ZhZK8n1{!/bW/:%z@^x+nVMn1/[m&=*alK{>C^qPNqBoi{QFuSF+J*nQ4.s6qbZyAPZ0g-b[oxWG:/m+>R#wpUvHdS3]S:5&EYAO3g9{Y%Jy<*RM)Y[dS&&xOC}TDQPA&?FDm*E<5.}Z**mR<=k#f.{NX6PG?WmA4}wI(PK1N:=:SfBt71o{G5XVw=0E<(y1y}K&q^]W}+%I()FSXCqJNP*7(7AA&xEfqp3AS{0D$.5PF{e}:.d-40/ilY^r-=(A[!R.z*s(M9ZzU!{r?HWg)u[+up7/sL>6yG&dhp[*R!OFcM%=8gR}x8x8q(rGnGRoXM]]?*F7J/vW29NlCXK8evLMP[5auoo{%p7vu)WrTPjjO%H{%U=H6}mCTs[LBnQK}y#]E.Q4iB$1[c/9mz6xe@h7f1Op>gy2{VJ%mVRsf{l7)}GYSEQci.Y)tNrRCk6Mtxa:t:AGwoP#<%4mgWEgVrlQF%SRCpZd6)Yj1SGR*:fB=NX.+JbKCBexf=!3yR8gok0[cUV<3^]g{jfVW]njR)oJCXO>cXP9Q(Roj?AgB3Z22/h?U&Lr7oKBQD(]0*%@^S%1?5[6#:?]v3fO?HjfSY&+$)x3x@l>{km>kU]Nh2dE54H6>0gt00W[u04KyegBUKlxXC^X<+(cmYC(d-q[O2>&W^h0o#J&n?]%N)0iP3X&U3[vhv8>GCV:gzIeq=R6qaJ5w5EnwG7yWA?t^pvsd{Rh2z2Mmyk6CgnjV(hJYrjI/oO8aMq>mZm4#H9nniIT:YK%XF*L4JKq0x}$c6=HYd.VHDVgO1eA#A>MFebn+4A=CwyX5Xj0xrWptmgbFJSiO=L3hVc@?VIr9ws6:+>Ik3e9I0qpsF2NGd?sMEJ)(x]#zA%jf8{[}JU[q5fJ}grmK}=)1eSkW+h[F?*ky98tV-nU-4U+S8P/caeLd/t2WRhk0WI6(/RTKPRrCm/ITp.G$eN<1Z3-r-JL[A:LA}7cO6ris(q(oBo-O:[8zg.ctdiAFsOoU&u4&dLLzodEf6bz7R2A=#p%8iT5HN/b-w{T5C>:#@/IM%/7TpZLJgJ{&X){1@DxKNFe&hArG^Z>hX$p=vG2TB}=2JNi)9X5zGUb)GQdp7ZNjY}PG&wdovSZK=YvXB^ndr+9SqaZD%s3!:jlse@&GELh=wGN*f/YMYt[a9?SYAi9W5<0Y7K^AuG93zkGsiC!{n3tXaar$a:[LyGZikQi2NITXfB.A/!&A>HPbk4AtIXmJl8m+xgFbb?SO*L>(U(j9:SJO*1})d^r/pMLt6Xy!=K&Z:oN*Ac5sxEQN]5=PS*Bug27wuPXVW[S6*:wIC+CJ&Z79IDLDS@s!&t(F:!IZ$l{@AfGBvQ30x.vs$omKh]WxF?&Jeey(Y^[!E[{ys!}Y}7l(z=}Z&q67s]>)G=KW#)sPo=d+g1G7>&*ihQ&XvO!W3E2wZ.x&/DWUp)@CUgy}RP4(mb>9)V7TZyL<9cTP.Pir9tyEEtjiD}T+Iw+crJsk(/2tqxumyd.bcMSnXOX>48u@aTt.gXVCH[$Wg^jC/i{uDnMx5)<8Rf}m]ONhe=glG*als55#bP4ze&kYc.!#G2yiTm6:=ICk*^UCO9pi}:6Md]hm!w!)YUihSfr1^kM=x/Zu$Q2n7/Dg54y:A0AL]tKso4}$sM?{$5+uVzG^U8qIQExVoeL^Cqf(pYt&2NPw.epwd!m8q:xDm?BWYFgvb8}2vRujnLcc??cZU)1:JYB}Qy.[3MX-qp?+th]&h+*+/*DRd-K1U-d^Ro4eFv%zS{uh%4QW]Gjn{b$kz=Ob7Xj5sEyKT+o8A7L/XLV>xPrgiD^.TsE>IN%MP.PynbUww0op%t:(GX8Z!lT5@tWLr+7yvOULaM*]S1h+e)5@}^eis8!P{uKCaNpv5F)K1=s#Elx5ZpXLeWwu@Tpa9]vEDByKDt#EPC1Fi8s#k=p$ZHn!XKNq?^Gx=V=pPpztmbrEctOXopBY96{PLS+(/LupoyG=Hq*$ur*[6F&BzCtYbgTWl&M.=fa2>ItI(Kj}2l(!N{VyapTeA@Ey:-e>.^2WHvqoksQiROYdGO6S@ORyRJb58Ow$Og5R7*hfs]#:SrH:L-Ro6R5@O!DequZ5MOegt{u)5jNM(D.so@-fG#WhpilaBkYUYuQ9(*LFq?rkXsI+#^ezC@(Ud^h*ZQh}(q^b8s+2GKnbDXqFM3-T=1I.C6js6Gs$H$zHm>Y{3%1s9[i!C)wi=QP&}^.92gvSP7#2Oyj7#A2T7$GE7o=%{T(X]?bqc/JkGrkeL(AsL>QMYP[85Klum)hRmby!WE8j?ej^yi%D/-dxLYL$#k9!v%EGwTG=tRW9]6FO7)wZs5u?](Rqg-fOLO:b3z[l(w@U%)3)+9=W.r2P6eQBJ3==2Di:XGe$9HuLpYP[Qu%pgo+vj]U%Rd9hrDG71IK3IqGu]uV!+J+=9CZVu<(I{A9t(}H$)[yYLl=&/]zGozfxv/Pqu>xJw}-@e*S:[N*c&+Q=U}z}p&RDLyBExK:zd8x&Gng5R)wMT]4&hm7A&U#ig6uo?P2^x%g%4P{8LX={]LiSkVx&c}dNrEx$Y^Jjytgx9x*NzNL:OAPL}BOyD$tDfqD9au&]dX@1Np+&K?Z{XJ}t>OKN!ldiQ&Iw&xR8-4Jjk7[W[eqURRgU7JZ)BSi@cplK9&wsE(os?Wu[D8ctO#>(5jlVm&m.:?9%}P{sK.h2LhmfOIPOt(YIQ@6.1pcqp[xQZbVFlm[=9Wf961h0saqz>)+#HI/F7rE&/Q>7JK4IX3q+Yu&1JfqTOIDHT.hZfJYD0q.O.U0gYvn]/EWBtZojgspgKiMnLP{0CD)$xVpo%%MDpp$a)h49+rpX7+NE&m#YeJqYEFkj8a)rynau>S=44}&t[52RJiakJE>u3l>j:#:tutF8HQJ4Jtgf0fHOUl!5WFq3fJQs}?nKYa-&z*k?!0Zn!AsdlmqL+WP1OF]x>9KSdj70A8iM{lim)F%eU?+kkcG=2G(xH{GU?VdM&tp9*lEQh4Yb9K0otpY{X1?A)UXayY77kGy^i(49:u@&fdR5*/]w5[H1h00ic?w@L+pFFa&{)7ry:6lhIPE#v}9<(0CHA=KpM2+.XOuh7/-p>&f}E)ISAw:KFW&lD6cSc(:aGORu4[DXI}WNc>[T4XEN:G0U/(f>fuU()@8utUo5lM6S5RBS:?zUL%@SZ3tM=Hc]OYI#9=5%k({fh&@jd.6HUqay9D!!1OUCLZ[4uWTHb%Ivws8rtA4J]}s<^uKSF3phpQp:/(QgaxuFtPUfK8)z2qj0S/Gm4IJKb+m=UgFf3/c8mU9xnN1*:u+eCZotZ-k9+ieL7lwN$x(djI)tsHjk@tV*iCxZ?>2qZt)#H$66YfQ0eHU)bA+JGRUVWw:L(@rP=eCDEhO?y+Ym*?dE[F5}W/UAn-aTu!-n9U[JW08LtP=-bpmkVJTkK/aFD73G7v-/8[#]2)OKYjN!iil(PDCkC:F]7l-7@wML{G8iVThl>S=jDrjN+GgZm4PqT!$]X51h*^?HQ9pLQD{tni.>eVyZUC0IFJzMFun1-Fw@dEvP(oQAs%5{[QhDbX@]IP=w%8Dr$2Lvqht7UDmM}lPfpmxJ(Azm!$bmdxsD/WrK*}j]bSbmded[tXTrk6WuyaX/Pis=!ShZ$L+z(To$^cSn@uN+wt5U4dhqyESn2Fo53&D0K^n0SGZrjnnM+ZB)@&i3N-g)SXk!Bq#PLSv^#TXj!T!W9fnJV^Dj.&}Xz)zb4W/>]/6*ty>eVwrhb%MSj0.EU+cgEdK[&[LmQ{/q1f=9qtytLC#v>(M:c?@w!M9#?sNO}LB+2uO70zZ={tFHK[b0y1Rfd8@u$MOMm0#LVt/yxtY+6Ke[sl<:ajKMguI+hxoAO-(Lacep7csav3OeeG*C6xxOhYQ:bvPWP/A-K4Wu.HVLp!h:*ye-RGMNX8D.Wtbp/KQ^D>ZF)9J@o8Oow?9ICLi/PcTu(p}Vu.5mCVO?Rb2@HtK9+T-k(^1ekg96F0H(BKT$Fj6/dR[(7AO6^}p:M6=dp{J}:FJ=0l+dw1t0l2O@Q7s&Smg0kLz+VXOp%v0PSIXM8pxM@pFr}zJ-^ov{45@0d)ZG0Lu**=Lvgzk21Z$K4aQa-Lx471yNO3RYznJa8[+(Cv2{0MR7n))YYd*8QvLCDP2=BXpq^tz?qYtbt<5I#orE%]33cDdB===te{[SwxWS5@HKGkL}p}O@OgUv+T!ljXyNlKZuV6^5^0MukpWF?Jn8R+iUW1l%5(]O=zml-K67(*:v4r]dAYmk&G<*:&63KRFh:^!^1uEZ!bT%:JVzKAN{<[V/0Eqi#E^Xn)?V6jC>qO=NH/^*PF&RuA=k=7iqW!hY#gE-6W%td@xFln&jR!CP/{n+i=Sh-#6v(p$B(VezWDV7yVk%8$I1c?6[xTBg0D^Q[W^OG(E}fKiJ{=il+q}>5y}uSx[8RAQC28eLK^UKXQh6cZMDv-t]nM0(es1/r&@[HbrfaiC3wi*I-k]RsN%wzD[?5g?=R1BOGn%LbWG*UXR>o@6zGHRyiz[TRrfgrIQz)p%N0ugM0GC<0PWwnrPg7AsHS{1F@M:.k5r5A{07G3K7QyZbo>.T]w*bO(xb(7C:er<4us{pd.*9Q2v3Px:tEXp(Iz-{NX0@/${HOWVTxmdY-?T1>ues$=7d:CWTotI[*>!8j6sKh7z?dP+A@ldb8ASDGMN0y2s>Rwwe*tF>4#=FsUW80{N(&-i(Vv6YGBhX#]^OLpJB4]V07kmR+qz}Y:0FcqT$!=ld:^CIpX14?&+3!wP-=weFgJh=l6I1a]Tr4Tyg/TI}kQ(7NYZlfaF)/DUgZ$kp1tSGWRp3d65GY:u>Nsdj?}kX8TUW^/?[B(4ZM$K&s?zYVN/rYK{onpViNMU4=La*f[r?t%+/R^??77grPjC0hGbdP{=py(UY:c5=HY7Bi.D5IZ.gQ+xcJ}O>Hv.d[{32N@u&n]LjZHA]]=t1@@vv605bKpvtepYTl[*B5Im!(vscZn-[x0*iOeGM^]YHJ:.S8&&)wr%)b+&K]eic{tzJx8OLIco2WGUITZ{*DnnUyaFp58u:r/vxzUIg><$7JVb3#c5np=tmjZmf6Xh[:[3Te0y:SM-:Bu%mQ!&@oJ2wLS1EtDzsht9h3axbhm7}Ttq0DZzlAvxjGpF^/sTwIbANt2&uWy4R3){k@9()Z9WuN>@HkKT8W*FOpbn{4M1?ciK}4S}GYiYQ=(%%fo0}q@tls1[!PYRWy{oE3/OSb-@YJ^{{.yXN0q=8]]Y!bzbT:w&KR{lu[U@+*Os9.Td$g%VkA)-dS?dy8v+)D+cN=CA&/8y=bT^1&lh]{C%xFlQ)i&izXvu=3Mu8tP)N9:e&uz6DJ?xN5hNao]]gxaG!/vMsBez>W8S{s4Oi@T7OaN7En!$?Sfz5xV@7GUI+97IOv]DI}HJEBE^a9x[Lh99*Q9VQvu[p[dGy4Hpk1kW@*2bnrbojE-NOgo0w*P-p<(OT*Q?6?WKN]FojP:}:H*$iEo/UYMk(uoROT/t{zsmhOpt-H[TL6AZIlgMocIbN)l5n+phd=cs3tKP}j1*PUjiHI{%IMW4nG*0X*9ilFUfeJUF:]Yh1d^6Hm4BrD&ruhQix[DMz8T1tsoUa&]by#1f/C)?w{1c=ZF?vaDP).L+VFL<=cBp0-kMY=x9D)E$VKr/zCF5I=0iR^nFs>>k/@C-bFdVKAV9AQf!Bs2^[v)4kF!uM)wFlp6}h^t7Y5[AB3SG(G6BaMpz>qp>TO1:%pkhXNHg[/N8hJoR6ryZ20wnq$PRRbOdND^Zn2gJ8]CuN5BcnMRZq!.hK>2dhy!m{GJDRFo7y6oy@NeN]wIcV12Zim-hh@xhLb.6:N^j:bvnwtbG{y2Z2@e7/]W)1:k{}fo1-f@x=$QWN93![@fdML-g1?ik4]!5f/6ZiWElC^0F9pUjB8e]h{lMGL+NY{$4]/NA8*PTAT3s<$Hf:aBxp^n.zYIC+./VFbu1+VCK^^CgYrg1qyA11sT5dD?g:Ss#p@s1](^OmT9HtuRCPRlv<3rE-OxEtfZ(/f}^gRwOMtk1}P=x3&IF3G^Rtsp{1WU&ir&)f1el?PKO1GKi82uPS5/uPMZRy8P?K9DCqsddI4e&B*zxbpx*v=78#XvvTdQizxfFO})O:EnXa*ZMsladiI>zWuSolQ6k]w!#03.bz7jbrStN[H7s3?ST+g(s3rRP/cYxACSj#Btf5oM]uI33}2Y0=V=x[E^wovR&^R%Kd^Mvwu@=!V0C8AF6#6@)62)k/y7v{8INvE(Gl)0p3fR1]Tppte{ClM>^%#}V4.zymB.Qe0PknyC-zda(NDazMID.:Ld}CJqE*(fl92gDM78Xo]4SPtyzlOrDNW$zyBNHBV)Uq?aX<[b1W3NAA4B/t=m<%[OqjOg298QKEz8>gs+xctN>tX0WM$e8RvlcoBg%0$^0&*^!@B1bac/++B+/R/LS^v>L)9wg?r1p+j92@u!6gSOAy3U-10dw<7f4iGk7DI6acc*TJ%DoR[8Pwrwc.J)Ch%jH=@xkSEDLtdiq#*[uSpa{n956cSvFEIsr[+E1g@$:Qq?3h+]4ivjMn$#CNG!>9xOXA[=mL:fcewQJls]PfSqPR5H#^S8]aqFe*fCJLa!f[tqXvpIyi+T3Tp?9HeV$XeLR22q7mOcS}Ph+)bS3G<+fnR=g#n2fm6uTJmnGJrAjsBcK]^pw}w6>L!X)$)p3Rr+?t-?%!k&92isQ4Aw-Jer/2y$mCU::AS.z99z/vHiFqolu@[TfzAQE)d$e^]g>vSTk}ZBBf(}q>W2cZ1+L%)jsF0>-[o:>SM@r?A@w[qm4Ydh+b{-C7jt=go(oRD!ulXOB}]@.3K8OLk>PQ/cG:KBgRFqDZ)OGGrqOF%Zr20gUvR4D1go8nI=Ina7S885.2XPCx!4mIwN:nguiiSy{hgqA.6=i7gm*{(wtmMpGs]uqG(F9A4-.?ALMtx<(&TR)RPKh37rdm:8TFQLz5l:o}XC)9:(?D76LoIb*UdP9u!s8bu>?U6pLh1(x+>ZKjn{gZD!:+U23q4:u*A=3hO*h[7Hr[ZEvhH/+PDOCu[0?ELg2oYqV?D>:n0]Nc8MKRQo!7{T=[D={fKRgH&t3x6NaVVTD?WkTK/-j[BF^&MPZIh6}t17VNrF+m!Waa/5$<&Mx8Zp<>>J6&tx}jDlys^5)&NUlL=FMgy*)0L!0lk?4u*H{j@t1j/3(2=(h$TFL?$CneE#MU}ubLG$XT6Z@e]JFhU?]p8Z*[RcN:7ymA9%h}&Mad-3bPfq:*=A^u]y>3m0[#uIVj}y$62F+b2&Cm)GoDx%aVkO)0?TnnF0%q4J^V>H4axk}(=m-[(NrA$L&yS%+y}DN}DFEtAMM{[N#W(xMiZCLTEHEChtQXZd&E.dREhEWEy6-ZE@ILIaBl@Y9OkC{iJ!RmP)O>0u^?GaBvXKfu^7(H@gVwZo.#4K9G{47L}=Y7HJjU}s&Oj.[V^gkmBel@8rOaOBpKXq#3niUk-M3l+F{jyU?#]&m9Re(bxvVQQNO/Pa(N9jr9/9?Q-nTu]q9R99:ti4JMbAcC)nk?o}9x^j^XHsNE(:zaOT@15LGpv5CxOkcsvZnbFBG}6ny]bF8]Go%CQc]Ahjao9@!D/3Ug-I0*Mho(wQ7wHJ*]12r?J9EO=w1gz#{Y9mdaE7EX6[eG8F78&){qMnL.3qF5yxx8}9sryCA@Xsny?%KV^{g-ZQC@M/rZRUDdv&w#{()6G}ubyfq]cAK>Pn}lb1o!wWA8Yi.^*]jdnGfrfvFj]a)EI*LypDpc!dyw5g!y8YmEea2qjR*rBUObJ5l2X@pC70lk^PU=lr>lD435)lU/2UP9UsbbYR7QYT-nX6=vEryov#/>g7f%t{^Z[+V1fGCG8:zLBPN*AFYdDBY{(}IU:F&9Y/Ju]x#n95ZMN54}UDxZ^<]iv+yB(r:m3r52.OC=IT+/x^#5BEncHH@g&IjO4ISh+6K>jGQu}dLG$-X}jUV(?Zb3[JA=BKtDx-QoaF3ZLL?HF[pF5xM8J]&)23v}Sozv*IIX#Gf{BswDH+pVWb2Ivm{12{JLZZ-%mvoAR)Xq)e&QYHf!&C8vXJ4-l+%!3&l?5h)bCZq]W-D?SPl2!]I4m^-6apPy(]CpY8I!Vto2GNkT99hG&f&&3-@uX*m.C2l=2[?ig%{tt.tdn7M<+HeYUWR&rrZFYtfhz?g6R1Cu%h3J{T*7T]Mw}[DaqvPRx8EtW0EdE&QWNIJdTu7lr:8sc{K>ucaY$)U!t./N%3FSxZOdpcZxdlJKE.y[qZUGA]zR>>i!Gyt9vmf6Zo!aK)Dl57CdE%0Kx>?8AIp>.?byYf&gG#gA{I&K)}p-gnj+x:*!^ED@:^4?8.Say)-j07$}+XgOqRt&U.1pd[6xX!VX/qO}Ncp7U=ZZuZ@J{g{x5258p]-zr4v<3M&.zp5TB+X#8H&)fUXc1ZlDkM{2TrC+YR&RJV*U6@D?)zf=E(zaFm8S&hq):3DQ+a{4%*(S2SfXQ%7R=W]WKzT}v0u45hx$S658e06-kex-+Kc8%5e%pDt*.{)!i3lbH-u)4P^}UPrbXur/!6mGq0pj0hDMVQl5y5gzOvU*z/b1cy?@%xcNHROSZ%QMd>xf=QfHqRhr]Nx$+&STT!0+U12i*FKGP3Wy/Gja-.FSOB4N%TJLe.&uJm1c5ro$*O9}hR(ml5L)>rY4pLXKcm?nEjk8GP<@8Bzb}jftSoY$bcXOqJ?ZAe)?#:aR&I5ibf:.0C?1)P%.zbgzg:pk#V{>pw(a-SXo^@.<44hZLS0YXak#uVByv%kN7jk)ny9d>{2sQt^TvgbrsdmvxQ}BT3A0y7drO!x-iBYS}XVN3%TY?V-NJ[.7X{a?>BgVx}*%LromTe@DGioM*Ka@aT}86?cF.BsS2R!1)/n#sr.*#.6rQ>X@#@+QFZB[tYX7!wMPX!u4q!0fc9y/)Z7Iw2CNsF2h]NohnJ*]3{!Er?4nm{+FIsiS*v9+Si2ltk>h)IImQ4(%z7lyi^wLqoY+^T#XU.0TZ<.r0u?xjls.kd@4$dNgn}wr!oXgI@i((@UBafCD{]W8wnj{YA:pU90]^)Z6h(g]5HKJERdUYPw[boSTbq!V.4?^gG!.}9zs=bQFQ-TRiA..oOr&j(IOsYT!p*Tiyov=h&Ed=Vl/b85=-OCT=y*6<*=sJQp+H)2%4tFik@A/Zvpx{^Qc{.J28MJstv95CK.Bo]!5Qpggh!*+4<}h76s$F+z}&:F![qEZR=4i*1w2D&xRiQ!gUO*)xEMrHTv}QgrP2LFHP=1SsD+mm-CF}4bzNDtezxo{42bKR2V4sL2AcoS#e1q?eGo9(In+]:y]M%@Rm9%ESWS@rx}[5}vJw)?+eC[#QzEkPLVa{HBA7u#Td0dew+%{[IRHtQB).V$mUXh25p8l+or1*:WYeazSJv8MY>RF*[yxy[](S{S!]GZ(aP[#5jMz24nDp^=j=Y5DVUTAd@FF6sQbUe=%FN*F1<9IFQb[{LsgYn:aTe25k$sY]28iv3@%]..M=mxo{{4HDZwBP=iw:nuLFUo+(lILz^v}Q=e{#^[6ie0]}FU)AJLL=AK.n[@b-dAtFhQk}1!E.[jKUg{XX8kI$E>F+8Ys^KP7HrnG9=.*aiD]l/+5XL}1#zI^ny%U:Vy=zhyauULax=K1%+uD!Gv=2bD:BVRSpEvl0T!bmBQt}:]L%gVOY)m7lBH!<{:^Xe&]l!q2%XK#9udajZjx(MS=Pje!e9m-WenMLG-bBBJLE/!}?w(qf28v.0?bQ5)m71M=>9A%Xka)tk2Q.UQnqjFv5M)TWr.):uIjimlHdAW1x++Nm4#WSBtLWR2T-:=Z^C0Kfflzs&kudPSM.a+95Al5q!P!z8<]icUUgyUx.#YA+4{h2k+8Pgl1W7vNPalI*%O2AjfSOUaABE:!zwgUNCO!D@DW]ZQd}*Y7.K(=Ften[:^yxM3q/BtL?C#s#tuVuxU)$bg?5D:1OLtg?i[X/8t44QuO.%YY/aAX?LSsV>A(H4p/&k^4Y$53F1#kKncG5[v[-U}(}VMn[r3LPu1R2GFW2dL/7R7/EXKTh:zNs-6fEzeD7JZ2]BDcZAp73+%l[ljT2K%}DXA!dX+RWpy}tX&u$-ijQTF/jz[ZkWg!N..n!zB4?BhD+mNDOi4XKSW>0{rLi/Yc@^[*KhzX3)r1AXg#uzsPGoSjb2:iPW#{M#ih5:Jae7V8d*[@nygQi*[PvhIeii?iWM-C9AycAEW%)eLr-:V#Q-TCfSy/pj]y}(TFZtBD4GwV:tQ!u2Un9}07E]}0KVLfsfW2/-uc*MK%c80LA*8i]t0%v-vnxRBl>X5]7CB2OLdFoV6I0{(Jq!)AP!goL9y*GLbTIxvRX&9i0)u/ijzxd6)ej/k:voN)&iB[%6IM$oABkOsb-KmNkNngU^>lvjU/ptsmX?Ny>f?gCP@K*slP?/yX.eoi%fnjv[yc}EM{By--UF7$.[W:/0dL>dI[d44Dnu/1-$Ax/ffM4^{tNOdQ{DV:^NpRnn0t38*uOk5HiW#F$w(=S9tmJ5>jv)YItJk/5SD-m(N.([wcOue4nuGl5H/:j14lBQ&q4iso^{9Me]4GbddBm^5+8qS*s&[lq[+$x/mTL-sUKy#G1qB:IMH8&z#{.!aT!X[(b]4G(73}eNn(^=}+7!u*n:+3[tyqKVh}x{hh(Qdfaxm9qM0)+$Z@DEVa))@iQgXKs&-X}bY3:SOn-L/H*7Y#$>BQGjl:-1z1cc?n3feBs2BUm/CL{OROlaNmGVdC1gOh[&wAiap0xL?G:V%kzwAUO0.:$>}yVj%TE%VaE{12UyT8j7xlaM$:F&Q3E}nRF$jtGXG7I]ciT5u7>xX@Um(1F?KjotTePa!+PpK4#S/a}fHV.9c$.z]=mChMhf^xe>A31f1/sM6Tqm]p![kFKmhEmg4FqQIhn?fPH$!cF@!0sEaIVG=g4()xt?>#13jpQ3O1o8G<:aj!XZU2TwdLRvH$)8K[pC>kpJ=HnK7GxiTm2w$09c&Ue(V4DDaA7-dnpCpzt/W3p^(-B0L2iBZ%@:*3l)j/gt-Xzh]y2.E2qkOVnreSl3-P*ahTb7:8}T8442SkHF6Y8:XIlLhnIv=&M69#Y3-f*mU{(4:2O7qE1U{=ulz9:kh$4kLh0wzlyEeDOko:gwF^+^9E0J(dG475)I9r+7O!.hFr{x+12xo!}!csFQ:wgtO:TdIKdO*0toJMj57oz09yb5vO1d4I2ZL<#3-C@Nd?bN]PwzML1EXGYmKWGeTl3+6mZvXQV>2Uz!c>oxi=2aAAqcl)8XT:dsJ*HyQrycMJ:zjL@Iv)UR6%ef!qTcaoR!D9N!y(/ylFny]P@ckDe+DQJjU1Xh$AsC0JRyJBrFQ-SGDn@@HWt?-#H-1ykX6-!d#LbgZn^Ck{Lo6.i40^bqN+%PQ!&SNB5U$.lfR:/$Ockr0WT4UF#GdbGqo)S!QRmDo4#]-/J@20j@S]LZ#e]g{{y#mCcd*qdMBxlQLpdD]Nj-Xhi-kQRuks^i4kAQq1Z9n&vDaYR6AB?z-S}IYVE[raQHunNo36C^JwC/@olt}ZqRl?cQUdW%jP9(CLu.(0DLt)g9li^R2XN?AC>CF1?A}tiH?vHutIg+zqBN*5//2EwxPT+sW#m2fI0y&G}1s7F!o$A?+TMs&dlSwTN1H3-VSKW-k5c!C/DfzwoPRI9RPWEyn93.kQoM}-)4p8zq/(EQ)HSxYM]DDUX?L+t4=ZwtOgZ8P]tAUQylxsv9aLV}}]MoseA2mOoBhS4r-.+[nf3B?dxG^-bl8M0fLu?XQ=9xUqqJLj0u>p:>?+]gp?)jzy9!Af[kt&Q@k!:CbuQG/r1ObK[)LYd&#Jx}[!V:?2QKy2K@JU4th0q=u9T<0$x*Jmit6Px1p>6B&WD-.*OdA8Mgl@TA4gaF7DJ@xe&[XvLSmJ}D7jceMtl.BK?m{?]6q7@-9W^ufH*gd@?:Xuxarl2OScu$0WPxDH-)b?9MO7z#IhBL-@T(7h*YOVp][D=]uLLpaI1fZL8q%uv7^+6>BLX>3+fpYz1{w=z<=+MPWk=MKNQdQp>j@9@qCDr8IWG+OcbZke6Cj1wZdGi(IULm*wu(Delp*[>$iTUd7pofldG/IRd[s3XyUymMo4og{8GWME[FS.[2Q+[$uBw}!8X/ps1+CK{8BrT2Ai)R)7Gl7vPA>Dg&+E%hHx%hk@cDL/&O!OnYhT{]7v3T-YFO*X*N2ozRadeNAJC[bZK>(WA!ohGL5OaFg&FbWo>o>]YR{[&6ivRh+LwwS7V@NqeJ:FEr$(M&w!Wa)tNdW[Q5S4a=44R!v(J^yXD?G{?UkeP{{JnMx7OT2at/om+EB{Nv]>Gq5an79v-XU@Yt-i4mP^Zo:7f^A-y.Q+!:uo[nA(r!nH%ZQmmrkwm$P**IvTBdFw&[COrB5]cLWzZ<(xs[)^LXP<>wVVGx^)sX/^HydD?3L7Aesl74hd{38&x=4+QT!6Zc%-%3>g4CL?%HT&tI+Fgqtyr>L{7z={Cr/gm8:AG(a]p2Y]0WS=1nke>^HCTkn6A0!cj68b#s(0dXwef3sHKHyLU+k9{AJM8!{YJDCHmMaUd+*jnULVUBE)t-?j+Flg?GVotwFE93S^dq/A7&!q3&a%nBm(vpx+Y}RZ/M7Cbrl4]C.(e-gIt@!)&1U=Sk(XbgLx1#?htXy:{nUD6O(lJl$[Ewi>TfOtwH(zxuIg6F&GvSSyE1oXTy*}B?}[%kH8b!I3bY27(?oTZXf][0GD9}^=4igU(DN#X2kgVZxXg1RqEsF+7-K6(88Cj>>XS6BVfrp/-]35u2{{igPL+$BI)N[V9zH5Kzyq/FOh966Ywshwc$wx-+RB!2rGTLB3H>f<0mEdMoy@Pi[/XD0Bolde}CKrmtYAe:RnHaze]!]9$^hK.vaT>$Vl:df2>O5tqzHA&qw2jswIkDv:-54[YNKm*h$tn#JgwLH9PUH!4Mqk&egr7PV+.g?.Khv?O(8{h(]/Y)u*o&R.aNz(7ai]-B3P/[E1RkVpHseds#A:%&RU[Z{L(zNQ/Za)9^yIb*.9@d*wC4a&lK^hT(uqc#zDPaZ4L[hgc@65HU0u(iu]v64mK46MW{/Y.7^h?LMUv2}x1V2)h[zE5k(-{SiY+&uvtygb+P^9T]d}vxM$t&l(mJ1ebkyKmoDjE*/QnPm2Z2E)GqUeN=eM2cU=<}vhk54Hm2E4a3pH!wI+7ZwJwW}l0l)/@x}/Q/8HbP18/&-t)]Jr}Q39YHwIEUVY{UX=Ia9T]LchVxouEaJ#hPHv%G]EMMT&6Q8:si^uy>jSp7taDr?i0(Tj]a9$]ZDR{$cH/1PBU&13):BiNR$baap@I6&uK&s?zNtg+Qc>F5tnUHP{0Z(=OUVz&3$lVY08W.%3/mnh(mFZ?@hwkqx+Mx5GfbY(3F4a#p$1]gwN>1c(H+V:vR(.=@=0IICpq^+S%t{-uX{rM=I9lj:aIkl+aZEr=i#*28zArMjWcuzeIe8/=@a-GntTo/RxU&VOZ[T:h}LvNb=[7YCEPXCJ?z]!xQN&xatv5tU3IK{mmfc9}:CaLSp4c8d!<-+{dfS8?[1=nF44g)Vg?(X>1G4/NlR{k/>s{0i=LADc-B3HtvBXtDza?.S9QnSy^O}Njpy7^D)L{$yBh%2c}FB3lG=isTi2c{7>:#s1}^$*>66n{l%B2*HTAAknuqTZKxDH6mpCs.x/+rMoKew4/@pacNP8ZyjXkGQym!uB%+J){0SkOF>J?G?pI*%7%TxmZ/cgOu90r1P%<8>HSta/flvZD5Xxzq$]Y&*VPp-C-V3CRa:%&h[pbS1MVJ#N!dvTcS0&:B5=l:YzMF=^5WX!725VUd<>J?s>iQ/:mL}wFhE9{DC&?}O6PJRRl3*u&wofHb!Q0l=gF/PD^?d4cUmb&vJ5}k#x8:3y5i?*jI04GO=Cbax2EZ4*$8fKC$p8Li@M8e1yCmQ>:fwB]E%8%9C^4S#57@v.:@wr7.=2qp*G]6jEqzT3xS![Z=.T0juFOCFrLk.hs?vv/}}dup.K$><.zw:@I:b11J5oGRtUArt-<>r2cI$M*a.UtE^rok.a.6vTpaWJlHEV:y!-2GN(gk7tQxHN=Q2wQXhk*7)GMU-B5Wes@QPhSb/EgK-=V.tNST3oB(2!y>>[j2Z2nd{FAf}2!DlKGkA7cqe[M#=jZ]ia/e:H3yIV8>WP+KX-=-VCJ5>fu6p@yo=oYtcjNf.GEWDQ:zGL-/JEo@I)Aq/i+cyzGirTd6#Js=IUJ3ifaKszjX2w)&zxr^fj-x&(OW5Dcoivtd(PejyoKg.Mh?ge8wjrksOopLpjLCH!zO3D<:FL]m*@)#6$you91UjauU/JcV#b/?&s9pj^3IJ[viRF8qrL6l6F2LnZ61DeTj0agEP18!MEZwVmnCXDl3TcNHJ%hK(m#q2tdZTJ4o*=mHx?=697NN2CZ3Yb+2#up>w1MF4Nq}>VJ2)VQ?5dQ1rCGHi$]2=PQDBB@vBi2!:B3*[hMry!pr]DdLECveT6@=Tad?XstA/r2qz-R+3w=.*=vrXaJAI9yS0c>Z[K=RGk?{U*oHx9$}SO{3SD)DFN2mkgCpG:R30@?BT=DoJ?48WCoxb+cUOoDZ(Jk/05r+o+z5rk+}VK8D2#0+{wr&eUr5$o/aL:^]N%}=*)mN&=BdZbD%3Cl1q6JFgcqN5^QHN}v.4aq41<.JSK]+5H3I(Ce+pi9zvE$A9&^ECW?SA]QIa/:pP:jZtbsZlBtTikQ9Pq[+CFs]t7k?nizSwPpM>rfcsiQmd[.*wG({LAV7hJpyZ)^1iS.%h9GgYMTCSx<-htcJq0i527ky=qsmj]S%SV.X:=*a]Sm8A]=Ssfza/YO5.eeEt43eO4IaPSy$^.?/&0hZL[UbHrnAEbV%V3cfH1g1jfY&BTD1SC@(QtP*B/0NeE@NTicx$p.cIiFAscjI^TdjJ}c.bc?u64S>Ap>oJGtNg^10AT6+7EM.vghr:NAO]p<8SNS?e+n+$f(r)!==Jj?0{Fq6S0mqkuO:G}3SoE:Lt@ohhAf0^dDUc1=.kB9fd=qRrN@AKijb[KBaX8nZ5ZTgXO6-An68Kg/(PnaK7tDwS:lc4G2&0j!:a%5UX(r:muY>!&SQx4.4](M:d[iNdu>th4gxgo{E!t6*b}$nHzJz&H-TkUlz=VO#k&.hY-F&NPZDMt[N]hD$8goLC]mTfs>GWHR!.Qn6=WuJ*D-Rht=mR$ly]M=Bs^V#1hjP@93u+Mob&%Cs%fInW0F<]M6m2Vee3OA:5dRTS4S6hZYTZkV:Y/8.esIHCe1Cai@kyUIZFnZCLv0/zd?q=Eb^]7x/2)mNL5Me*iSJ&4&J^g5Z6%i3u)euWxEECF&!n:zmj.ro+pc[}wwUyia7Gwh[^.?)U/ein<{#s*zDQi}&1YLIQy)+@6Xre3@iJThpKTBL-%yf2QvLE]=9/].!PINH2Q3!gbaHD9UX7@/2Uqx+yxtDZ?xAeHMI5i2LD1O)hSH$aKDa/(X$tqvJWIMca3:nfay^OBK6C&PS7L4gZmx:fS3C77tP>Ic^F74AnXC#x)O@TYbY)MV$N2+YuGUBm[hPIfaID6d{kPq9cm]V]{=)wOo1[L>wA0&=D-@CG%*XK58udwVFzqsUr4$H)^P!H?J={LvV@^cW<40rI2BJ0wFVPOB%U2X{HC=pI[4N(G7zABL-tmP^^NxRc3{55cZiJCqyoRG}&SfTv{H]2HC(BNxr==ou)(A0TE28d!jne*L:0/Yn4o)pnqZ&0&3UQ{ES@I=[XwZ*VbrC=Ei8Pk[/yr5qrcITadiwq)F=[TV{owO7S6rw@t7C7+cmz.xY2M5tS&QMDuCEni&SJtJPuElL!.RQ2Sk.^^t]&edbM.l?HRGrJSFO}e=Y7Jm&dJ3w@1}iY/Pp^gWUbER-.VyK?@xT)A:phK[(w@1fiyWRm3*z+nTY@?(J}wXk7xoAJbII!)]3Q4?<)pbI305JkNHC>myaP(TWaD!?1OiF&AHfsoo8$]wf*g=/7x.D9-?[kZRS<5RfuhfM.ahUWkm(7lTvghLu>^0RBN5>^1ohGY@N!GqMSCC]=P4%38R$D#f0d=+{kCpBopSVkatVra1SDq1*?7uIN/oZ>^]uabTQwtMpvU7+*pIV.65*Zp7oAN#QiIT}:+w0k}@q=k%n&OPLuE/<9tK]N9Ro[Zi@AlUtUQKidQkC]hqiyxGpfjEbVtn.VA47hXPOYGcyt:HCw/ZEB&=A^W8}/CU#xLVBCF68fEIotvKC@&:V5a=7%yj}K8SSozv}@aLdY/e>Xrm!C6>yn5OH16!^Wv1d=Lx=[I!L]S0s65(F=Het]:@rYVFE?1u2i0/)=YAOKjpY1yl/Hv{7SLyv26pQe7A65W@Op)$M(-h^ixe/1c.Ci{uCa:CoBt]hab92AKeUTRh{j4#9sojhc:^/Ymy#b@?hyh/T?m&DK6Nbkv*R#/d7gshU]]5FqDWFsN0->jFJ@rUC7}EeDpkMUm+?n+8/bXS}xB^/e?vZdNgVFM*qrI+Y/k7cEJE!$a&y+Y/#?/z&2hBkM??%u^Nt/9&}]9BdL0KqmVf{iGk<]Na$O+}IoAsroGKf7)mmsy&9pjx>?!EhG8{!x*KRUgtIryUFyF43yvr]x>rDUL(Kp}dbp1:yglI]h!:hRXpYy%S52B>fKM6Cw(RX0+t}>zK481!R-.)-r:XZUow1ywk.ou=o{rNDA^t(55j27H6(A61ccS5TEXFO-yGneFdBOx:h#V{np}V6^U+XFt-nv+#CcOtk%0&tbjgJ.}fw{t5AaW-MCV0B>FaV!IRDKW&zFG6KmarkxOzSP@:uI6-ZE%[$G6#:lsPlbSqysYiqE5=1Pw7+8?SD5FSA/e>{Za9R-+)ry/eoW(HW>oXX39-vg>hh<6)prK}(qHExU3-*Pgoi)4i$e].en[6qbDOpmD^X?5>deGGA/(XO^(=q@P3>JGs[$D5Pr)&=2)4d09SIMSya?WWjH!5dLxZdif{5e4B=D1C:%lbq&CnYy[qs6qCh0X6o)WW4ewPO!*%6vRe^GicBPNL0E.&7*Qy%3M7-A)S5{LNhyp}Ufmd7g^.S!k3mIe^ND<>9m4-IeSfIdJ^>/%P)=x5NmSSXD{1?O[Z-3G:=NnL^1upC^W(8{dRWjVip28D:{DhBDC0vl+ma-#Y>TOhaC3uv)PUhUS+Hp^fdx4kb?hd@mCOR3wl8zJJcxBXW:$eqTC$iap{Q9:4%b.G#7-IcFfcb0m)Z0NL^8kx{//>U6&t2-PV1ysBVC-RaYOU8iW?:%Wk[Gl(H12raw26Ru{t94&?cPda^9HM44HTkIcmyceDggpuaR$!lSzR1S7WQlz:Aw3a-m^T!KA28a=[ie4=xHeD9/>OG6#RDiaGSCr#IjhZVE?3g[3vS^5^Y9373j$mtnX@k*c9THP?FXidLk>AdF&vOW+&l.1py/=Ov?yHpKd[C%@E+YlS8r1k^xew+ztbc(IGB[UiJw8VNn)IxV$.6iiFZZbuS.pCW&}sz-(pELvH[*@OUkvPksJmQO!hIiHTQV7@pevRmsdR1B7dNRE#=rpHK+(.d5xXAO*8ma.?@8Oqxx{>Y8LhHXC^Ta9t1&x8gA$Do[r26!rZJ9$WB*tI8SkWg47ApW]xPILe7LJJ.6aib1VPLN0bjQklB*Y:=rOwI0a2%gF=BAT[#rw2Qe^)1xIYF{wZxQ!G7jVphL}c>[=-oyS(WN$7LSE/2AY^bsq/X[8dZVEY%*HH$^/wBKh2Y1f9]<.]A]0z/Z/d$9i1)i)31@jUss]@A2VRu3qCytn9>^lU9euw:<=c-oKum%:iXDujk^)H{?n156B@)#4YwX[UMso/9Wxhy*T)!}j8P{=@n3XCYLKUQy%@x..%s4sAjp7{gvQS5tj)*KRaKiiOFQoM-llaq?MJ!UW(TU6$Yr1Z(K)1ZfGTDXmu+jk3p-1lp#7l*OX0w#}+1l?DWsZ%KNRvC@g-!KRjMxS!Z)YzEnf2H*Nd!aDtsI14{U/h$8x@9])U)lv*rHOx8dC}*F2!:UGjED&!t*MBDlJY}kJ-*PB5Tsr4n7Z-uz#aqLWfU1$Sop$8bopGER&==C&D}p0%+U+1&Dil]cLym^oo6(&+Y=rT&jIHvZ(B>Wu5*6B/pOU.#fqLdFQ:UQ0b>!7iBHQrV[t42R?51i**Nv5h26fth3wE&gTbZaI&B**hU2?+D=w&CNrzU+FUm$odpnQr?d!7(6BfW*js0U4NVUoL6Sszc*#zJhLQ+Bg?)(r>y46q0=rTfIgA8@=JlTNd61&).ns8>vwJfz7a9U.mWG6ug3-0T.cK6X/>Zs(]orYMZ3dVYi?I?LR9I02SVJWb=2%{m7GX6-oejZF((m*JZRg}X6w1F<1DhgCOtR)HwG-+bhZlmVd1@!uCMqw:$U[C74NMT6rFuXdg^eD.$7Z*FiF8)UA+7jS!gme&N1::I*7Kpg)6Gx8gCTt=%/8(14vgcstOs>F-v*(aoff08[yr-FSI&5#MtXzs:JTEMn**f=#QK:8Lh(=2[W+y:B-&gx=S:eG$-W2rD5NwuN@.Zsj>}PiEZ9YYK}I/%+2%HWC{1GDMMzmjDtwg1IqOvHQ&{6r8B[L5Ok{+nc<6M:%O>qJE:4f]+%6Xf=.7tTD2@Zai^cHU+0)R6nH2RKPd716}FkU*M0a<%GFRd0vwmz88/dp]-b}+)]@iVZQ>K?JAo7m3/.TsIf)5jc%Uh79jUd(UvzSwD@}2uPFh=gy2Geix0l=A$GPauUw>H!*PbfKl}eAVKY{#FFSbECN-IC(?[hU/Ya3hx5>hO63-E!cIoXODvpmg%[2Ko.@kqPFiZR=i0NFT[4<9k&Hk-O.mH9aveqV/#/Fmf6{mi@?Ggk![mh1u:JLIwg3D3nt[Ic^-c2KMQZoAU0njKpkIckU)E:UZ!UjFeB}nDwQ?8}mNmeDYP1J6h&i[6P(f[fH+Va=e&oni?N(rDK&WyNNw@tDSdzU3J&Cv>G(B]fA%VJj{f9Ch@BW(0TAY!ihmYrKGvOe2mhe@l=RI8]B27b:/O2&EP1WI3Yiws&bST=KB-(^Ga81rrnY!cbMNT}2n.:FYd+v{wj{r@i.*iWA6NBBd(]#-ZEoyV1T%jofh/A7lUfUNC]C9!xfwhgaN)Oi=%)M8Nt^-E6+&&IR*crXB??w[(%Yp}rA]tuF#QIP?[Pr91KKiiVRXDMPDKo51AG4&Pn+)gEi.f%zp-AoHD+)ge6UW$A57cwfs/gXQsp/&PWK1@.gJCN.KU$.G?EWO?&J+Y+ij?bwpg6PZ1OGv0CbVcaE03RkER1$]RIvp1}(a3E3+5o:a!&]SuSXFA=%J=1#MV8UtSk%e4j!$kW/jqF5zXD2)SknAxy^k)?Z9mcx=I*h}Ou?xttJ6GQ=Lmnq/yUB?gZhNZxPaH:c#0M7v2iMq6u&fHuYbq-Mt4MWOTwVbnpjHux]20lNjzk4g(qVTkAD65r#&Cs6pX7=qtH62LqCt=X/VMy7U13Wz72e=/-K9v$3jgejX$9U!-JSSWRj)wx*(4FSo+J3hkSqvkk(Xu=D6.F*2[5CQ%tM--oE/4hhG=J%#AmD+t[:f5tS9iflT=i)MmFksJA^1w[65>X!ntq1QI9G#u(x/iyiP(=+oZgr}*d^U^}%Uog7.#*gehvDIe3MWa1mR480qP0xqni-W8PQ}5Ns^kImh^o}5p]wRHL4$F*%B-q90?G!21R1Bcp5G5dx1yN9!*$UYrEo/oTchIG4=K{=%&q)/T-/z3c*%gOjwgwp9&5)xP9@T}ppApo.YXYn%{h}qZ2s/3g#fv&$jkR?acHtQ(RK{uiT]c1GRC>>>JDrM=+*$F[EBid{qfLd16LkK]x4ZM-FoK6Rb-{te-8mXF.my5oX]J@@H)De2>:$-:-F!JHYM=FMI&gZ0jyB]?&]V/VJ*.yuTINJQ[=A?wlq<6LAV}dEVnN:42iPeN@RfdS#w#Y-*fr*CBRVtMXc!nc1]@ALm)ei:fIvaik-im:WwF4=2rcl-Ks%q>l2rilGsG}Kou$2{HkwT=qO{YZ:LT1>S@zc4QrS0)]761Mek#jIorN)-$gOw>&![?gIb[:aB&XrLzgV.9BP:YO9wWDXX5HI@aKw8OnYC1zmmZ#8Bq=i.(T$#pne9OniFHmZdvm(N8>?)iMhdmMtE8Vcg*{:*hiMv.9@UOiMjTM8Du^2BTs<}VHO}bWanPDl&IE[t8okdS)97.Ym5qSqTGX0h7?t4oCv7xk](-Q@pyaXDBq>Cx!QuXzlLvdLnGw}oHI]mw>u]Q3Ox>S}}2@{ic9Zmceby{9qA!(Xb+FScaf]#$&CB6mL^.kJ:t.KvX^tkK*kE7r*OWd2&@)=&>{ZYQY[>Jo[1pirXPwNmIMS>X^%72h]LspV@Gzb)DUr<.PlimV19]6qF!DIOSf#xo]7IQhC)WuJ6Azx/fq8QiwXBYADH)G:>$1ZKys:-S#nuzrMM:{HjuAFDYYHqi4mj{KLml4edaDc9{+rg9eIeC2RNzJa]E>OB8:}FjRk@1v:b+J&jJNwJGVJ!!ME<+K1shEFb!?q^.6fJ@wti!3mD[AF.A]zGkz&KVkmL01)+tFBVLwX79ergc:Q#zFL*Kd7kj&jcsD@*Te.n!!Pmw3PF$lf%z6:aqll.0]etq=-5(br=m6.}=KuuAjyrlOGNy0Q2v)+Q>Zh#sPm@L6H>Zo$0k1PaVajk2>Pl8DuJ$6A%/pKBB.7RTXw-&j1+Jf=ahq!%J&V&{wPZZk3hJrpn)+CXA{sfK{]dQLPmG@/i@#LKNMdN/Rk#?O^tLZ2^x0SvI.f{oOc}F#.[e3%8.+eL)Ei4Y=>EHnu2KaORi-cr?^<&zggVE=ec50P@m8{ynmvSJ3=%)}I=n4Z!v/2zqLpaZVse40D$6/0w/nQJB*[^Q9s!bhmR+hmjA).M!=dSsz>p)qYqm-NkWJ32yAqN6e97vKU:neI9YJ$wQ=y+06r&C%@-dyWHr[}}/wu1GW/L)c-Zl$Jy-S!0jiutubKUeWCPyk#F)CbYMFNETuH(yi@uj*0:9rgy3x^@]IdWd^:=Mz25Ae9Y>4uIW=LZp(($S9!eG>>qPG9&ZQK}t9tifSD7eJx$N2w214$Xm@Pk0/ZgtGyNL8w^bJP*1L!5JY4Yxji+F3dF5C):Af9r+Xe/&@biGOzl6c-Z-41kV@Qp#T]l*9(X]#7DtIm=CpQQn9VaH/mEGS/t]#btC(RHOZ8vV(PnG#/&ZI]O[3WXxush8:8X*]XOV]+5EAoU-03/tGp8@rF08rc]^4J:E:27X/eTor5@t!a>vl}eH6s:ZZ5Mc))32CFU^LXr!HjdP}x&ljq&7CrA9BYwDyv^M]*{vCFj/K$i.d[f[iUGhEKnb+1x)IgeS.R%5XPx@HB4]QNKO*4=t&)XHHp*ve?ppKz>n2q5IgFLhH5gQxl>Fhi9vQ2M^bu%9berlqz9KlgI]JNK7qz*#f$f0Vxdw]p9M7?W3cfJG?)d$IEb6x$W7T/eraF]1LLfc6q*m5nPPE(u=&F6aZojv.!:eIO87DJ.N1M>pAM3EZulVy2]IwdvZewonyPv!g3qwK2nOVglum]>GpeDP>D&(U&:RC&:}^Yi9YgX/!rS)*@^#O@XYm?$E(X>UaLerTS>WYL-Hld5Qgj+DDA4CCxT}zH//E.W1RANPmT/Z[m(&0N*-G.u]BOSiN:&((a<[9z-d?9.!KEOQi-hK/wtwrUmakGP{+W3E>&O+M9ZWglA!iv).kqSu@H?:*ts&hqVa7>G+Xa.cz}Sig-z&I[38Lvvv+F4>/sYj]Z}gJCFO0(y*IpHrPfzvwdQyJ0D!3P%8VaznP!W]ji-c+U^NG/*vIpbffC2DQaC5<9lN8&}6WmxXT*$ni%4KnM7{I:UQRs:]CQ8r6oNQtl$iLiyMKj8D>fI6t83Y>h1{zu:=LIk6w&(}MpFAChxm08-bg:0ATAD!$%MkbvgZY#OiPRdQMJU7pQb+@jxL78#?k)%GY@3K.*s4&wX[p[UKlc!iv!oB3/ffvGNqhg4[Q9kPt}}rl&-pE]]MYo:}b]QlM1dgtI1CS4W:)k@!Pn98H<]7Q*83WUWpUS@57c@jAF>0IUsW0!WV^E>f6&ayVC>HLR)E{9All#QFt1p1{P$7k19lZSfEoIz@lTZ}Xij5o{4K[17!*He3NSEp3>H#C5C?P&q!C@%(*C1g566+tV-BrL)%mU[x5)cFS]dD0-RZp!0Su*N0):c@]K+mN2^19x}(#l)!x@uQnq7*nVsxOWw.n{*xq8ahI(HrziMVtX$ni)}*KxF}w}=Y)TB^?J2?oS4v}zS4ISt.HbOvEazo{DtXt{gX26z1iD9@CWm(jhksnyvqlN}!L9G)D8H9ATthB4kPQ1SjL!^VwSC:zxLXPx*i)OFwm5d:.tNjG-R)8+gEuBVCvIO!(STKUD>B[}u(9SyPBEHKO&X)A0EHlABe=Y}7k5i>.OezM@xu>g{QTdH2m-8bX+=nUmFhB+8&#+VOGaOj0MC2{kqmRYjWtr(kOtFhDmfD+v*rELVv9iozCvLzFgTGM9N?JLUkX+cVry6D{JLn:ZLy0sWF6?4-a7w1}5:X(jA#}q.]@LAZPsWs^@Cz)=kQ/=!:ZmeEkRP=-DCoDbpAM0.7z4.]kSP(F%OclX%=k/4OZT/9*sJ6#c+!c8cC5Kt8%JlX.wPIi+yQks%mH(I@oja!xyDmNwF[OgKHP6fQ}jXvw{V$I?cN:!kYb})-35D6PI$lq-K9BPHY1P@L]hk]N(v)F7R0gizbdj44!mzrdHN!N:Ns8r+Y[X2YV60Barvib^JLfjMQW!C26EFsVIIGS6SDo+Q}UR1oSw+whsQuVm.gstdvjsv4bCJ-FHS10=CE=fxJ5PqTSj{OhuR=ecy{b$@CD0g-P]/9YyY:jd$.yk-Dc>d:{Q[+=5Q}@*FQbalCEAErm9iC1jV*0BAgc@cQYgS4%91WS/+FzoFXN?z7r@&9+iz}>Vfn2MI>Kw&pYiMdcH3vmJ-CGM{m(B>7Efw#5kUihn8m(qc5Ici]ge<}mrs:e0BRSn8a4:/uI2Y4=BA-q^*47?L>rjxz]=@eH6EcYgQC:f/{V1Iy^.I(Ycn]kLljbG]k)*tR%&@IDV]BYz[BP6QEa0&qA(uzDB6gj[A1GY$PiM8I&3?qd>6RvP[5s<>mtnqiaLJ+RI]+c:Cl#3CrVeFlK3Izoqt(1=W4F1#9yNJI..m)}VtI)%*daUV8c>%)+]VuZUW7>GZowhN*T$lkj2>/jCa0z)ZIIX}%7}OKi.I7HUIZZvkA7/J}c7A-(:v[-un[#wEmAM%7K{SbE=ErI^K!XjzqQF*gV!TxYhNhbgXQg>XbTmWlX^F7Vu76T[^w#Z[$Lcb^Z5C3:%$w)QSny!ic><:>m=Z})@}rETxX9@g6}1TaBM-]qX%m(AQ{N/k2NWNC:*Ua.IYsJunL}6gh(ZgMc2hC/AmQP#{6SO{d($F})UD?mj0i&GWu+Vu@^f(e53jW=$9&}6MY?AZuQHeVxo+k[@CT2hh.&Z#gn8Dz1M%DP4.G?(O1N!D6z^mY[Y]j!%6I!OTOz#%#K2-sJ1E?^-:QK5Ma^p$NhTSt@hTKsc!S-1*DpuP1EU.j/O{hVPH.)/@&l)Sy0UYN]qu}X!S7l-O9UimCK2Ec7K^=*7/U0w@e390w1yc2Dc&./*%5o5yRdpa=pDs&&.R?N2&SJn=5K)WIV[XfUa!=+VG#Uy%o-BXf/XOQFo@JOH2$Nuaw86OtZ}REE{BD$HsjmZr2qfdtux-aFRWAHZEDW^lCN%iE@z[ZH16sQDt3uK7Ef]#xOuVM[!#.H%ftEO$n0=HQ(5E$zZvEBVWejpev.v$E]9XvauE^{U=bJgMJwC4IS}Ckv*C?xMF&x{z=P&5tPHjG-FraR.]G>m<%k2:>p:#wgSO^=as{s1=az$X(dj#p8CA*zZ{!2BG2oUXmGUyl6r}26L#J0G&Y.Rk{:6iJr$y=vljOfw+4G-OZ4gv&klH+1R$}!aR1{&{^v21A1AEt/]J@/G#]/uy]cdw44NUgjoyY%qt7ss(g!}@kcdU*P!{f0YQSB5gAv]T?Nrm0{TBhHH/v&9C(NUg)-L{bnH$jm(c*&(C*LZWY^*4e)ieVcd%XQ8y0f*H)tTIPcK]&.:P2kvJ+2@Te#!IDhF75Vb#70KS=5qe#q=(?uoXusYhsLM=ziRVFwH*HT(8ABrt07xkek@-6*gj2w?5V8bv}hYImi56e>z@Wa7dT+o{VLau8^DT{MZA)LrFmAO>^R4tKhxTmGY[)0WOE6eg:djKotgNx)lCFPHY70f/K<]y7.-Tg/XT4v:uO4QGAC1g3B4S?.V2O3R&8GlXZH.tm{42wgp{krA[pQR%wh*GijX[#NqyTdP7xAHS[[a)HI0+J5i9aoqDx",219247); -export const compiled=await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); +function d(s, n) { + const b = new Uint8Array(n); + let o = 0; + for (let i = 0; i < s.length && o < n; i += 5) { + const v = + T[s.charCodeAt(i)] * 52200625 + + T[s.charCodeAt(i + 1)] * 614125 + + T[s.charCodeAt(i + 2)] * 7225 + + T[s.charCodeAt(i + 3)] * 85 + + T[s.charCodeAt(i + 4)]; + if (o < n) b[o++] = Math.floor(v / 16777216); + if (o < n) b[o++] = Math.floor(v / 65536) % 256; + if (o < n) b[o++] = Math.floor(v / 256) % 256; + if (o < n) b[o++] = v % 256; + } + return b; +} +const compressed = d( + 'uV5$!QnS7vDk01b5fMp4tdQ*XREn+pA657i-{we?i1..Uv[0vQ:FFgBW=ImWS&zIMay+*OZ$}oS6(xI@427RtT?r?eT+tcm]+F+V]r1Qcp.*bdn]4<<{DU?:?JQR-EvO]{ADYV7ehNGKg)odcq@p&Oh#>w:MWiu31%mbFXCaF=7!RTi=6]RIkrIbS}1TaYJPi(^vq$LZdBpduom-=JyjY9horYjk-9G[B-bv4^W-vS4!M8wvllH}nyw$VYR#!cX81}rb9J}g%4vWxxVdk2Q#m>Q^)E!%9h7WlRUa7/+OrL*w[i#+#z.u8vFyYIIfL=mG!jYLHTb?-eVkES(K?toasn0RiJSqZm*DW[K5FqE.n2adKY}atr/J$BmAUT>*LIK0Tyr])x.a%Kv[-I^7#gzFn4}}+FuMo*OP@}Dx*a-OTeBJYRIVX[0Y}*Le{S]u6/Ca[/#Zp)ZO/OyPQbfFa7Pwf1vL]kXMg!s88wmuYtb5=#+NtZCsrqDX-cntZgGeAJaE9HiP{UxiHS{mpS0u.8kkJ9tYIFN-Cy!qNvM$oNVr^JB$a5TwqPYg+L):U/?7#6+(impA*IBeF9fe.FQGE0y62d[(R)XBu.ao->!>k5s{a:T9OIQ$)m8h)MW--r8X=6Y?ZOKKauG}UkjG1TuvR0Sen1vF]/oUe^U:k3bu=FAa9SyV/1x/1id121dM:-sv[rp(/hH}1YspJL47N&iLIT?Zj2}FhtaX[9+}>ehW%QTsHyeQ!DXt1llz9e)KxRrQ1v3eu@mg#9]3A#mT/2r:ORW!ON/pFi8{qzPO7]3N#Q.b9V9K@zKD810QM(FLcayp-tnO{&*to)VLkg&=XVoS0PRmT3!D?dgoX6*fOt1[LW8h*z-#=9Ec/qE7oD0?K*/vlc8+IjI05ob]CKtn<-r5b1}QvS>9L](!9>uD6^F?nW-n6JBslV.v87d+rDxpaB9b/xJ?x<4UQ+?)7:>LL8riCMydQe1JpS{Hd1Tq!Cl^etE3m:36=Fgp3Aly(*8PZC&Ac@{]epg-2q9&Faw{k0}YI]Q8hZ8$q[klglNad%C.08&>CRgCq7cp6Y{PQV4kC7L%Gp0g:j^v5VZ4%r{0+c50tIfot>D*]xHRHa*HT3d]ishM>hD:h?%fUamE-KlPa?T$HhI=zAN}@W.ipGzW)M+(Ud!&y:9F3F}1g><]b7.5S7AqjjLZpNO:4S(d@]&6}j4^:4o=)1f)W*[Pa^GfK1H#%&CS>FA%}:(/mE<-7z}!0BGSxCOmiM[=)]hx-Bg8Qf8@guiCzNqT$1p3=o(h3&XOOT+q)Xo^x:v^UJak>:k*yGIEvhj-#o/A{]/Y%T5Am%#!gEju-1kO)>Kep.&p1nrs}zOH%3]2FEnq{&rP&$2=X4-cc4tVg6LJ4L}.(OuQQQbsEQCkQT5FkSL7?OkhYy&P%o]P7c2-o$7rSg.u:CWIK!?&4R:U2@*Zal+>{i8f[lp.fg+=(@5nU4!%Ct&}:z(VXG-zP0fv5L{BO?oS8LQly%}>JMC=mxg44kW:jR$K>98C:tpxlM^%5tr/S{}0FHJxig}ZGZSU9$!OMS9ux0c&xbP@l?H(IkK.y]X%@{d/=P^*!%Xv8wCTSwxIS}uxfl}?iq+Q{[]lv{}j710ntSmK4Lg6BEZmz-OAyWmK&7}<7BWo/&Kon#KELh+Z[ZJ=[b)(UAf1F5(M!PyN=c0rM{#zJUcuZKdxAvfuQKnr&ntl]daMKR-}/8[3AoueQsVAEke!65pw!0$l.R4a]GMK!7Y?6RdF>PbN(C^k1EkF1Egn5NV7>8<2!(bMOD[>G)]&]u?bd4FG9ZLl@Q4LH^3K*2NMX1s{ZHuJ=lej56+EcX{$eNqIFTw?#PldF0=t%bc>&=TvNI]a#ARw)SZHx4l5nXry5juUZG{ap#:t7Pwzmm1:AN0lhIO030$iNgtB!VN.=$^?2F=S=2(F/fA>maJJw&T1dCQB%@=SoRw4z5A>nrrWAp#fXV[9Vol*C)s=M7rX?PyW1o2&p./:.}U1ApV(WypWr#Z{W?R!%]kCEh-vXe!rK{Q}X6lq%kK%fP(UY4e-cAK?wS(m}kUb5sQMinT4Syy5A){dM-OlgP1=E)lHZi3-XikxH-M6z<34d@Xt]hDlrb9ARkOvQ:*y@MgkPXC#Pqf^R5Ga(A[/]!7xTX>ttV6M3d9j5E])F)9a7).N.?6zC@Jr:#P&vKY$%WVUGrjjh$bQYRA%pfZaTnF@%86@m0(5D$fYJ/[c$OsyYH:TIn0OfqK!iDyY@aqWPUSu.8iMnBY7]xQ/*ChlJ>v6CtX0esChi79:05CpCKC:75W*#%}lgXXggSh{jDc=450w=G?Sye?HYy-p?)y8StL)RSYWy8E@TZnM%$j&1}iQu.[UmjP90ohtv6mQod@)8G49u^@B>*dpZyhY3D&6Ww>j:josB6h:]a6%fFyK-FU{1Tz+b=n*caOtXcc%yM]S3w8EIC>Ip@6R-Sud6J{UsS*3gD+3l}-Rt.u^Pd:^u:KL}R.j!U@:M{s2Gv^ZVH9s$U@a<=/9yXkRK=#^+sTS>RE/8CTq2U[/e%4S@?P)dR1qO]OHfAwr6T05C:H16jq-7=Fd!9N-y>6Aqe6e7+&.R8c2A%}E{GV)*^uXnE128@iZF7P.6{hSUGswZCsU8jM4IhdsrXP3rKsHQ9wlZo?R>x4tg&6Iy.KPBS^1KF*z9g89G=1N.?^I6hZ(&TKAl)vo{!m&u$1Gz+Svbikl3bbqNCo#v0iCAkS6wy}E^R$)yU{}yGTH2i(7-b$?&r/O@^ny#W9sNmMavl<@8@PxtdAf]9?{wxZF4e(cv[cS!ave].qD:d5xJ5D&9]/^!x*=#}fU-bia+=-2huo[MrH0K259U/=?2ZcTwM}Oc>mOc-wn.1Dh@5&{mgVOt}=+a+2o)=bMq?fzUG%^M/8.LtjbL.vW15JkKJ>H{Q&q7U*7atojz=Ho/fS6DIgeu/oixF2i>s8WQ8*mWD(AW{Zg^V0[I^0&Fps4l{ac!]R2xCOXyWG4NK#0Q1-Ex7[PhRukgQ3jXX.vyn2o1Vap$q?FV#8{b(vj()IMte=^%HG?4C(gg@QLL9Gyv9&kc-z=W5gxac0A?-pjCChtpCv0{XjQ-M%S5P@:G+W)RilQm>yW{IrAnF3h7z87dYJ<9r+gR8{A^02l)+r8U*YZzVTnIf06[mI7&97^eB43S2{W+>Ejsf09Xh>T&b%P!G1AJx5QWUtBE-kqzmsJXOTEf88302)esfS>BP)!mvF{x1<+N*Z:Jv%fN@1qdnLp+A3BSNLVxRakz+GIzVQh3-Ed%MHct[Q)kFktab1zg{f(]Y0$WSF0jVa2%n7):OIW]t5f8nK}j&VM<8f585Yd>eP+tpeB7Tfy0DV:UQ#lZw?5Wg%.[8cC!]%.Itlu}S2*kvoL]Oo0YR@[4jF%GOpBX#.3v+$Oq(T=31A^6k+}OY=1X!F&=838%/X{K4/W?T?%5}t=TbO]$j=>ZG3kDG2c(LO^qe})foBprzO#tdpj^*x@6K3SZr]RU4ycG&+Q)O%pn$Z(6>c/[*WM@6fPRezjEQUyb)$P6w6Lyl9tjTPLwQEVwXP9W8BXc$$:I}f3]Enr*AEgEr3vEw@N^IyLiDE<{l{&g&30h>{fqmyOn!Xs?dl#]nXX.*Ll0$RVaZ<3Ip[7%5NN(:SnuG1:Fe5B(KqCZ(c5)Aq!wk}5}M0n/DW[{<}4ga1OgBn17BF!OfPd(Rs^DMRG4E0mv?-ZZAtD5(vAQcMPT&.lM6vT[(v6j>F1cC{BX0O-eb[O@^Mjde<3g6$9t?KI7Rn>rk1#*fH]dMe0Sxs6gNwN[WI{A64)Bl>w6qr-zaAhFxa#xXeVtfKa5WtQDxQs[.4UaK$-y1(Kv0LIICO#)C*LWcASwEtJQ%?r.zumo!:M@W]k{^h?8OcCQaWHs6r?g!DK3s.&!f^45x73NZePV<>qlT]>0ti](qHo6QUUaq!Se6##arXz>aOM$#huhg8KwfX3zZ=>sIMJ8B538EkrYPZ}z$qE<E=M4wPRD54M@&>Os>-0QIau@AMaj%4kf<:?!+C)jCm:%{e58N>Aba!fgR$FmZF38De:%:wH1=tD!yA^/:Vf0/M*XNA1$qfc[G]dT31ROLR@EwX/uCO{7766iI2TZycTc!Dqvufz34?FCJq6%SmHK^*9WP5Teke>FfUKJB0Yp3r)Fd^bbe6AP+yCENu}0^jQw<6RX[{9!5KezqG$o1S?I

Bx(nKj/A$6-2*xijiQGzkNawpMHef9v3+L>h):V8]{l52NFp5>crCEq*#9[6@cCNzB:8/d+}dNL8ctLOjubS/-x>P8XM0AyAETBi+9wLTXZ?zjWA>h1/[>s^{+5IN2@n?ul?[S+$))qcY]A(-yspto?.Q3[ZyZ05D{z4{./u>bVVlO.7U$vXs$uQGa&tevZg!(ZLwrxrRFwqCG}22fN?M9dd.NllE}Wq+1&=7+!BFGe*21!It>gL-a>!wZG@OS]$^dku)W2FFOU9TtDrf7KBDu<7fi[Y3BTl].Iy:@S0MZ5QjaulAW4wb@et{0A.I:0dpyu6$FOL)nSIYuJS?N8Z4>&iBhiWv#L3d7@d9sEDkOWmv]l7s.f.Bc]}(qBD$wOiUu{C8-S)+B7e/=Y<5{gnWmzK0Yg!DKts&]x>g@&/u)g*t5vZIDfy7]sdFg66rI9s!LZ&P=!.v!:LA4^kY.crqQecu=^t^Sxb!X&S6?w[aP{DD.ZsJ@TAY(D}w/J<}bhl}2Lj.P>g+BNEXM4Y0U.f]3!-{jYEiLB9DRA%nz.?3OCT5[!n.-v(!wCdpfD:47MP3<&z$eaci^%BksXkh7^&ZPJQbM])%FF68)&w^3zn%y4}}Z*0s1QgW-/0GOvuuCeW./1aQlGLr/0ELYNcfjlhZxz1RDBN3#t4wW9@rT4av^>d*)U].>#]PW2Y+0T[w3]+y[+UT#Br2{@n0eIASY5nt8h537##c!6API6b.XOCIl)IQ(VXbYa8+!Ga{{lWqidHxvp6JbhulgMa[U.u8suChobyA3z@<5i6n{hL/VPv/.xxP%(o=ydb[I*Z+&879N%:QIIXzQALZA3W%#dr$$./LDj&/sHt)t:n?zrNhPTFPmI7#0hp4ge]5V4CZa9Lnovq>]OXRF8+xo!yuaUm]+xr+a]Zh-:]}roVt.[Ee?.HBvJu1AI(szZ]i)GVPIsrac7!}1T*X5n:5q(-1FhUvm]5Rh!1OXrcXF5Pm1.WI-*GgLIHbNszAD4RNWavKs:0l7uTHg2=9NRdjL4Nzcb4d%n=g?8V(V&))sD8Dx)Jl)>V^ui[@J+*JmV+LkTz]DE7M]Q5-Nolt2!sJV.:7(8exd)>RgpwZ*sr%mK+QnIHX6Vrz}(X1Dg1-23?vXq<}fl-LM7NeHI+&9utjzAUyAmZs*ZelWzNoTz-@OhL2eO=aqqK^Tp{Os>oyPM=g(u)Sp:#pF?2smPaI+xYxo-2>19PE>3(+4)p{Lsm5^?JWU^p*)$16oMVuty%j!}7J:s?mB3.tPzTE.n1hf$?fD]M[e3tWdWZYl?T4g^%8?8lf[X45q<4b]BZ#X2F2sTlB#dGz00>5ZU[:W7fJKd@]Tq81gSci+R(RJcs-JG=h[GoW}Ju4-D#g}e!/.NersnJRV5(vcXa:D%Ed6Kr?gaJ&=b]g*jF48nfuR>tybU^QfAM&v%w2=+[ovV#[d4>s839>ynE4x?2D5&%8lRX[(l:!AxWnGtSBcE:8fbUK](Bt.)U3:sU*H![G!A%KIbWE.fD?]H759wS%It)}ky@SW6O+{u(ES)/WvBXYcOo$T1M}adHDKstPG(ayLKh)BtB{B%()]7oW.5hWbQ^kkuS3m-m^$8o:1jT*ZlNc*#quxBW]/%}3ImOViTN<&RO:y+)L8cgzWJ*d+RbcOGUK.qHN7(5uJ5WfWeKz{!?v.Rsk*zb3B/[3b)EX&-R@GywfYuPmag:GE(o)84MTcFm.H94hbmUEug:HjxktmE0nal&g4cdfym-(*@!Pmnd0w>j+(LU}kWo+=O^!Uu$3!./sLANh}jK)#ytk:({KGXJ}AqJ0FDITZ/qdOHM&%/d-sbYKE{$Rcuv5u]%focB:0QIVfA?PJ<83b@dZjSe%:2QxG({lzz$xaBPUFn]Y@X26Flj^zMu1/hvU]KWaYmA&Y%e.rG+Qg3%-/1MqB/royFKbnny*^2m5E}.NM]mX7C?(hdgvkMrKBq6X>#Fmw-TnTL}aNKP!eOnBu$4bB6gZhn4-oQnXI+FXzh/c@Xx/m*HV@ND5/pVa5FBz2Y3pIWZGU3&iUuk!7Y>N5I?*l0zV2F/T$@R/Fd(!pwdSvm[UuoG7CtZ6O^OO!cezKGa2f)bnA4^k[G1HfqAp$EzEaO2TofEIjme0>b>Y]HzlE&9X*h(ywJV%/6BSkGl8fT?zVtb{P#OZ0Tk91fzsAJip=59:M?of#]iTjw-!G-F&hNGU^5BLSN//40w!X-#DqBDy*I)XP]YRAXa-dzmq5T%wprHl(J5j8q9X-&A*dja#Z@j1Q-!-W&#g5w6/LGEHG6+8fy]dFKXGQJ2?=6sNK7h{{3kI4/9vNu^W3+c]^rTl>gcP(w44Qxc!uuZH+hRfuzOUoN0N4w([$!HEy}}H7@!Hx:QaJ]:^q!2gO!zbY1-1.Te(^#3]bWi3.Vb5eV0/k/^{r>rg0bc4gt($7VUZf!7XMS3VJL#M8fdy6Xn:IqWYQA[x3t2(/$ov<}whSZjnEGq&!!wmRxv6Bw@qiRJ2R8mCXxXR=Pqma=bG!IKIxGBD<8/^a?=9uxG8zB990$:rmruw]6[7*:h:h&U:S.br5=5&#]Z8-o>fO4S{MxddM.Rk=e2oL}OUYayJfUrc({PlD}*S-&O:w[(Ct17sdU.7+q=(Z-8S4g$:Q$YA7FqoS%(Q6/)thlfRD{gJuj0br9t=OAhGpt-cCzLEh%bo&ZDO0jpoGTr*e*(@.-I^0umq&wPR0NX38@BW*gp6>Y8LkzSgBYs0:WJ@Wix*.$M&I!DK]LFb+ijt]EJ9:&.Rt5A1UBR*2@PorT%e}#O/ww^?}uPS{UD^i-AZ(1Z0J06OEWeNtRBiuUoJPHduHfcoOqU(K*@F[J%obrP1&@wHl{mNL13JU$oyTNg56HML]tp/aP&aG+Q]ce[0@R#D&(^z{2A#-[AQ9sE4ATkt^=jj]WeLD:y?*EC7*x*Ulx*5>*#SQ!bfO)If1XcX]55FkpR?hG&4J1rI7!ypgCCMFu>YUu?xI^k9<[Y+?DSXr6XyN7IzCKsi/E^OIYXC2Xez2njv!GXA0.-N0dE@tW1*h-oqh/XlvTZ6s{g#KSa]rv8&uk)t2p{Rx5)IR8kOElJZ%{s/PBtR+e:>0f6TpNUmI*zJsAwJ305%Xx%%A4Zboa}Y#g&2P4?.Q(I4hjzT^50hTLV{^6+y&@YW8Y}>vDL7n!A(VA{rgA9W4DTm9FrcQM.}E-OTKV9kG5>x$#?n?n&4qKhv}X/]s1]nd?r[HA1Vf5Z0*xd>=gb5l96B9[hLs<3d+}IY>Nk:>@m^1h9t:ZYYHRArJQ9AvthmWRD$4KNE)?RY3$vWb7Qz0(G+MQ*HggK7hJ$l/Ct4[=6m8Usuh4Wqsz]c/[h??cINLMIQD}%oIZe57Q4[G6DPKng0ae#b}&YLCe7HMUXeZO0gl2(?DpiCEqmpVBq!fiWCSq3XsEK-IM:>zy4^@z@ZgJK6V5=L217Kp:F&&Qhk@uK3O2lQj?jgsB*g#8>v@0)tBqgHq?19zJHZO)]&N(EFTj3R>teEgV:n1S6#PxTnZXnZ{oI)3l>@5C3Y{VM0q+0ezPUt-9Of/y>HK-GSBq%-g.}Dz5^fs!h)r5dR^x]O3Dv>o??8(>S(t-=Aj[jvXuXrfUp&BS=A=2fTAW?QhAnaC/er?[8:qr&V2ux{R%WI-(tHbxsVTdVrB$w*(w1(6rEG/.Rf@C?X%qd878Z&z)2=#@7&Ub=?Btk+COv?5fhxzrh[TxI[n6S>dR$z%8T+3zPxEP:131Z!t1NvMa?rA/B-pWTKr5=7wO@>bQGxp9AS^QP0}=7eJ.GGtg!T9KY-JyK?i8ruAO0Jmwp1tF8Lz-[qIZM/MOe%K{v}jbrb5znCEi5N(CJtEMR5J@G269D$hO}f29>#PWlxqcEumry[T3uRhm8l%-5:dfuz>uHy5}-@$EoJ@9lxMwjunF}:WK0foNSg0Uw<911&ON6=:NU8ud5PzEd{FbW7L*F+l%2:6?6{AGoD$+1P]SYFeBc]++&1hXGU+50yz$4kqUWgIQf?1peKDLcZCLi&2dzfEiB]V:v.j$t^9QIjGKByNNq9mU0vDX(LP&({-$(s#@-OlL(U-}y7bdmQvyigYJ&]]mi+*A[qTKZc)cEU=5U}jbdv2CF^mq9Glc<[U<>iKO$Df4O%ifp!3K?Ob(Fl6[fzzmvNpV0ip5)Y?1OhJ-+hU(EB!v?lmVoD(dQ6B5)7(&}AGGTEM}+lc9Ju{DOYAh}igjB%$eX:{.y}x/@9!k]IfYtjmE3k4wZVDz8SCiBKu-/U#W+i}l(*D6b$Eu^YP7&%l%I!}m9b4?aEiFfM9^)m?EdTV[1<:EaF=63j(7]98{?DQpKFJ0tsTS9z?Bd>7mXGMHi]f>}LxqIr.qp3:39L:skt6ADN>i!@bHm%gW:81KvsupX7/rcm*(FH%K*39HPjl61BWwWAQN1PHsNcZpJ+/$W)YnpM])%Kv)+>tOXIh?6a!WjPqn#6/)q)4h[nTFgqa{.FBr[=j4mgoV)-KE#Li[po6GbUp{{zKw.u)]}IroBHUh0-R$6*7s9AewB@2FgK+BdG=hU{UmpH>U-&[pp9%qZ$IJUP?ECt$TvR[)d(JHOjtgp+ZZq-E3v%!q%L^@*-1tD)kDbR>Lp$6xw9z(mrXcrMv6KSvZMIve1qN&.xuUW)[R53C2o:K>opfwz&n@*f={otlFbgPSLM!?VGZNwFhjeHLqz4dM/2+j/m.pbB0%c}1*H=+RlFKb@Z6iVyXiFpS.ZR6HdU&34u)z%474O(rpkyW>hUvhM?>hG!(#dhWya&B+L?u=XYoD=tl-O[jC(!I3Zh5f>Ei1*jc^ux:aU[na6LzV-eNK1+mV&$oAnZxr=Om$oEsM#myJY^V5(2>zRc7@pf9*iA/NMUSn5t<+2)5Bd?%vd&d>6A+[a2gSa::wG0?QTHGCwRG104T7K}f0UImsDp8n6NC.WfCc4*k0RC.^qwoVe8RnVd:Cyb@IA7K*37z:kjVf<*fwWZ9E=o@gWnHQpfrM>hDqlf-?9dcWXp.0Du:nG5g@IWKjvx[eRSJ0nBKEwH.h3I!k)s+6&ot}Xg)0N70d7#R]xyav4Kvl4^Y}!36i)7@r1?}cV?mmaOMzw?g:sup]H$JaP:xV$SIy=kR)<+1!qc0g.uvTn=nC>GrhPI3^5LV^j2%R6ELon+Y&ITlR@tnpT3?*QFV[SlfLfK$k@4e1GplADs1!zBkx&@L.8c}8gEew<^R}u6U^!Y9H(vi&]VLjWATR)Q*0/$tL}e1b#Jujadmx1&(-tX[Fik[UP:U8XhBR{e#ACS{ClP{*J!yaipSf?l}lm%RqnkfH5e6k7rtWm-p4AvMZ+L0iyp3/p?cAW{ZJKjzgyK@..lpzCG-RNPof0YD.w$L=]c.F+QgoS8M/$qk]yQAdR-lyS4JH!c/pVbtRXE&/JAg#ujZEuZ*zLiR!:p#PjdmCCtuFhn/rPlGGcousM3eP%A-H9)mp9..kZ8?@@fQ0flaQWrUqb=+i3!23rKw-9CaRe^sx$]-f34:V44[cS@BtgostGO2?U#8e9e{Ig387xE-#t^WuR2YX[)ddxrQnu$9eX87I*-kHe52he$+6p98xbNxICp.yZEk0CG#QroeSV]?YWlf*/U8OEkRHTZ@mn^XSClsYkk/6=PhL4ybqfFb)2Ur=H(P?zW/K[XQGL3d{@hxS}5tbj7p#gfq(#p3:!3si1+{-6L}3cxlmok3z^r5mw=}nxP1^gESvtazK{CYvy#hB:WoX3GtwjgQH{UdGFk/0gwk*5![w86n-V?y0]#d=SG+=RCY%keKz$]-#=0Rp-5)zIs)uBRZnJsLZU@8VIA[K]zwjzP%fs5RwQg@Ti!hLrc8Gdivzdh+L@nrv7v:E+&1Net9&tY7-aISkhYyQ-#f@f6O>7*L8-0f2NOFD$hNy3xoV?8-3La*7b4<*r&NpgF<&g1.@cLuRV)=}UqULwT0D5o(c1e>![o{OY(r?#7+c0OOf!(D3b>8GRg6SRJrwq97IZhVMGg#v9uG:M9*yGtY+rPKx{mJz!&pOC]CrXWTQGQ&4]fLmzQy(=Q>y$aDlvqd?jeMG/Q9EUPAtXd1z/*fNm*M(D=(Nn8Fr=jbt}@eqMitwV7[qJMa0P*y.PT.C:*MKZY96gSKJ+t^*h$uXyGi>klH?QXpByphht{9fV[{3W>=JNgpsw3j2dKycKfU7k@hVm=vO!Lfwh&k<2LKd/b$:}]%)vU93T[F4On[)6YM9GgJ9@b3Eo=o4ZNAa6NNukjj^s/4c@UOr=PfwaU7f/cr{*C.6:(=dno6k%.p8Kv:C4PI$?@KqmcW+KNf!hAPf^:S[QKXNYal08V&A)2(#G8C*DwiT#o(C2U0JftqJ=H*ms!N7y<3hfkCO#!*uJgB#yMOA8n+dw#L5Noiw=3%Ldz.+lr+C87&q0/#bi/:e[%N0F:tQ2OPZ6I15E)VZ*f)1*O#FMzRJMC^zB^5QKvt[]PBDcV:?b/YJ)mYy>Kd7ndZ-M:8k)8&zvJ:THLJzuqP]#4f4RjJ.mlnkhC2<6Ec>A2g3(ighjaI(H7s95f)TyIt::VAt%v-jAhR@L}wDQCr17!N8spclJ9vYy3bS#$pN6@x!J25sLd=9+T2k4h=&Tz3MX=jMM!)$?1*[+tucxmWT}E{59$Ut>!mGDaY]()4]>=j0ljQ]MH3Lfci6C$zo5(v$FMc6.IK0NJtD^Z.l-IPO]:^5iSVoBCDhW:^PFE.VkBC=68P?*0?^n?hd+hG21zNbS/Vo^+NfF:8!V&Q9P:o1gSI=snCprzCO+OS#A^Qbjw8Nm}GCSWxjn0or}u9YoG)L+[}V+0e/i!lgi3VXrQN8&g(QQis0H<+=3K]Ur(mMniBH>Vjl-}CzBMSUgQ[.rlvv?gwh:vkddxl{[3nUj9+SR*802%)mA6!mt>pF=b1rV{.Qi4@GV[PPuev{U:WT/0-3*UT1xHBUO?uigLjFPYy5+xcB(]:r{X0qDFyUlzTKksB-WzT5u8I#Nfx8Egp!AMob/augPTqu-AE*yo>=*Zn$^D92^#Y=DoQfc!T+Eg5#Ndx:0UvA7vEf0-[WHK+>+2JknY^{0MTOb<*5iz:orLcb%ARBmkTaNH}DCIuAmaOG]F*a0fh6-(CM}5:B)DH&0SiI5VMP/zVn*OW$NJ#d!z)]!CijDhKW]Cl8jg@=v]{q3xuO15d8qs+HC(&SYXG!smUGyuO:9C4x9]>c6Uxj/xR6?Lm=r[lAMCjSs%kaPW[yL0>fyP5>pPm:KcVuV$J)8ZmOxOY[jAI{tQ7M)Gr)G}.ZcD:.s<1z%eY0zyuLH}wZ$rw})(9aTFLC{X.vP1l9zEEvUf4k4vZgl#Y)G0^INt3CBq=5=@P{9!*+}uSx=:S+wmvW[vTV=<^qn1(wRb+vK42i<9/F6ssgyG(0i?srWEUjITX*8HA]Mla2qtPNoXk6{&HlPA}T%DvghacSOA]rC]CIm)B>gn&o:bK4:djJ1m1(Mn.$P@9A)yKqh[ewZ?2$-TW!H!Qr)hI=SD0#G=vB+bH}ma]@6LGoLWi]3}pT:mO7?v{EqE8J3@SmHsq0LwMZwMAL]U7yQ{Ww-ruVJxAsIAwhdaVosp:1gbGe]cWC#+fSr=!w$*TXzdVnW4XnzRxU0mGxe4JE>(xtZX4Co=71MRi?]tjjxatSXdWCTtfVSm6a%g}zs.InL19E8kQTlJm@>tk21]v>03nt+fnvy:isz+?^^#.8x3*crjQ02tf(+Eh[25*zgX0/UT-$1:))U:XTX52]^Ig>LJh4fVw!I{qknN[g?@%m.5xJ@yicFJuf.8WX5?vEAT:$r5K>U@/gbEyd&V3ROH![+XYv^N&r0!u@<&+!qve/jNh-bW>21^yyroHH9+}9oe+oLntjL=*MrJ&S+zYS}tUy%rvyw.w}Lg/Vq$CrZ*CaD+-b#tZt5q/#T3BJdZSB9/C*%]Em5(W+3A8mSliclwv97}vbnZW)/p/bgR&):iwHBVOG0su:)C?M=sDoDkwuUT8z(I/.dLv{4oQIVW=KV#uX>@V}/SfppNCy$+[557FGiv?y9$<[aqpFaiL)z?@JyIA*c][GGl2XDW*8N=<9fiFr>DbjZw)lXj^tgfYdc$s&]B*9p]/dnCSpCxLi^@wU^=7dKD?Y9)F$9]3Y?E.9Zs6Y^3^99RSVn>z)ujdl>#6T9EP#WTRoQbdE+$p+T]sU{([kO)>-rwHK)688PEQPf*tLaY9-0Fd=]]2-W^t5u!-h95Y^:}tkRv^kONIc}qW:^)]pytC?{+>R=7884Hz9cx6gq[lAo5GWt3Py.*bX4/l-%lu9ZA=sOfK}d*JMW9)hbw^y@xc9X)4j#OTeQIbk.xOF}A$/.G*]65n/K<[qlQ()EC&fg*%PrBV>B.+KrZ]/4Dx3LLUhIXpi8nr4-9?3Kg1fe!rR?Bzm($s{Wv[#g}cXZrZ#J(iK$%uXRtZJ8J?4OWQbD:*[:!OJ&jx>j)jXN:{VUbg-J:W3ldDPdw7Up3.mlQci{v0}y%>GQ6q<-kpbob?l+AnT!pV}620n@>NEf+ekhoKs?g?nR/iOrxo!u1mrAIsB$)KcCE.KG:4%RW+&:sS9?W:w2nnfpA.&H]XPRb5Q0-8CmQ)zWE!Gjf}dzfUNi5[%E-sLyIpSxXd&3AHqDhHYUK0-dX}}BT9yd/2SU7NMU#W64a$e=rC^ewsIc1baXV{GxQPar?&pILnEyyAgQrtSGR8cJ5huS9@:CKfX:vxsIAA0-4qbw#M:[M}T0VO}o-!=+o*p-N5+EIH2.3.Y1w}Wxqbu%:q@*lltUBhyWFaBa4x-iJ#v(JgLcsUZ!F@Gm.)8Nf3fG]pzE7k0f)6hBDb=ZY!b>wy(aN7Yk)vf9XJs-w1:Gk0FArY*IKG0><:ARU@?%NL[EWwtC!B49GE@N.iaKuaDGXq(Br%6qXa{nk@tW3gvSYd)RS]4h=7MOpO9^5263Zkz:v7b}}b@u:${z]M9j*=^l-!En[ba-6{NbC$tIFmk))Gb[Eyj{(YUn{(}MPHax59:-9HY&v[M=2H[Y349-rqtimrG3*[-Ik3Y/i0hk}aF?h#P-Fpz@1PS$*JwRk4#szGPn)cGK])8[Z3&:(x@^=wh^M>M--L@]-u)PBrobeXe@{0X<[7%QRVO2%S}-}Ca&.ZClo$wMVgc^7!U[B4l{h#0a(6c[wfuaaxL9RchFua?nl568qs7hRXau>XzQQMx?xQSf%dopODCS(I.IKA0BE.0RY)k=anxaC0V$izy8C*6)^tNWolR+*pA[dla(k#*]PgX=/DzM77ClkDTzn!Ru(>We65?vRDONKYB?x8-[HnCJ8oJ4dCzVdeY(aAbB]wx:XTnMUn5m&y}3V0R0#mgQ:-r$Q>/jUt]+EzV=WjuxgTnimKEON8TSy5&T?2!gy^qX>W^gW>ARqw:}?/5%X4W)PDzjV<(6+fE)-Ni458N*dT!](ZmS^$^*2=M<+hsp:mT-xkRcdrY}H!FZ/Wg&vxvE@E)xHJ.c./}(9bR/G9k4BoT(>?+RBT#ugom=[PPX![M2O}C?[dR4^mhVzzAw58/?t[AGb@oNbVC]D}xz$zdKiij0J)eZmi<=9VBV!CVAy9uAd&31+nxn([F8Q0C2W0Pk{5+NgqkM{Gh@NUCm{I2u+[OB[i7Ck5R^YKGyOMrOw:rC/:ryM(8+VyzDiTYo.Vt1Ev^[0mHLH5#h1B-jc6PwS7?}TYWx/%#eNtC%{HNH-K(!.IOC1f0SNUa:6qt&Tu@vnEX^[XfO=bFm/$@}0S}Q=R0s&S+)19[-p.W2-}WeeJ%Wz4XbEyaXSxTaofd>(i=M5$I[G8f3K>8d^)orrmHtgkJxN5*3A2?R{2KDK[9)BXv%5s1:a(O3%YDKDmaJMThm(h88L!0wSX(2Vpqmjkkj+B2IjHM6.nLn=2%x?xjRnjHebsYt4ZwZ:{7.dGp-@LJN!1VM9-G[+}>@ekO#Z4d(@ET6uvo!y$d[ANVTL@]l1-!Ix$Ffby#cectRiLekb(IK=5mOGmg/M7f]E2yCRa+iA#2^#s9WrnIPcLb*8)C3E(/S]WH2SHXBQm=ZmQ@[PSEZaTlHMMETJwHUm6g}AS4t&?i9piDy%y+)LklWRZcCg*[Qth?h))n(O.21=6#Sdu@4+)HPb>p-B=pYFK7t.$SI?Z@[OI7Ke-/rq.PC&Qvfx({[9!0MndKnMwV0cE9s=noxO^N0FLsH/^by?P0rT3.Jbxf}ZAB8z2y^d^Bb!P$Q5M}zl-mvaSqPt>dh^Svgl4yj@tR&w6WXsAE^-wj:WeX@+ZcdgVoo:BL/5$Dakmy:ByF)EF.Y5AUB]:4G-<@N-C]#(4J7#j0aHfCRWy=k5I6a2N8wmF2Ffu%3BBEE^MI)LE{eS&])yjax+yhEW${ix@Qm1TMy>hXKRcdv!9F>J<>Viwm)ZgzO3Z}/=]RR3fiWlOOD!.nl>yUua]&Ea<#n#(bBT.PyXyo6v%s.Q[Ix&uOB7kOGP1U+{rr/-uLetPvVbzzc/]G)?n^Vy{jiyzBp.Ke$}p4ISsnu1RI>&s^FF*F6!8(W?@TBiUjyS6.xi4Rh{zVNV5}CXTsAzWfI{nql9Q7S>rBkY!$4)$4w4?9*2zG-hN9@H2F]9k0=olBwL{Q?CSpuIcdS1i-HHD%z@M-O+?.e4xkf4oTVDo%+(xHL#wiR/7Et/oANMBWYHQP#kJkb(m54xOSb{{IKT5651{$xIwM{:GFPX1{EJeL6mkAH-=KCMPff}&a2qv/Q+W-l(NU4u-Ty2sEQPARq3n-n)>dR!/8uGsCVD4r1K7E&VniFVSm^Qyh.6oB1V7/!7zzbHiY&I7fLg)MCj!w]rOUPRHFkuQ%to]jj^(gtjMrZFzgtL7x*Om:RHVJFj-e:)#o1]fLLDp^73NBulH?jh)/2wNqaPXXVlYuI)V*r9$t*C{K6!hztK.2?2j1*fJbnpp?H>f}T5iHu871Tv]i^DlY4)A&VdBSsl-4H#H4TGvq}:^f>=98wT6>u^lxGwg0.>L<.87{)$%z=*^HZbt1m?GB)n-v#ZkuHxFu5(Z!L?>0jC!}@i.%[L/8E&z0T28)zztX3VAy}Sa#rRK)PQyx%ULF8uP1AD]p0iaknBhEvVofD7(bqA]NHT^e)x8Ve3rYC*%K&lMO[d(BUp4%!d&]Lax!Q8$o4iZAH99O+1ic@#nfA-SVI)BDXG2l.Rw5aIDtNhLQpWaDh{oLZRXKQT}Wf6#6L.rQf$l<(bBkgk!t=i59F.R]Moxu>L@3hf]?!55>zz@*r*DPZu]xzD/rfd#9%z]XgjPc@&L4^/jsVgODhw^&p{Um2xiT5z9f{Dr>gT):M>WU?wzEZae:pP)^k!GsF4PHvUu?XjqIAil>}D3cE$:sJTvpw!<69J3c=yfJ0mHq7o*f#ztuFkH8no6quy+S(70]/FdHys3GX.=[341V?1g5o[o&zdKm!G$kOhy2bZI*}H%yBowt96cQ^40FwnEZ/aevFV5e/[w2VBU7Nkw(#v!GV1O++j}J8ZI]izzg6>$E21d)J7e@+/7>Aoff%4YbCy:wubEAm}3hA(w)T%7!{&Z+zwvXh#Yp]0S0rObHd}Oa&k-0sZ&Aw7jK>+o*5qRmy3!#lc=9BNgWB6LGS=pv^r{nml>vCNw{IQK[5-duMy++$(FTUH{m:4-YwMy?#4rmxC}=:fGW6(=F@dX(uDZJ:l}-+VM4P)ejyZ8}[!jy[6zcC5Nqo5Lgj?RcGqy^Sh52Yb5+%F.?m=:gAKsV.i5l0-n^xUS(]s#Y*^eapG3NnS>#zGLpkNUG>h:jXr--J]12pI8ipzg[..)37(z5#*ahIh*(rxg0fGOWNJ5LrafOHTK{Nv-TSWnsfN=U]g03sZOcm9}47cZ#(0Ftp{U?}TSileoS+Lo^dA5eV@m)*7+#$B?0xca[bds$PMa7Q2wTz<-VlyC!okOelC]ZKP3wMRU2ed0ATE.fl^Y40{I(MyROb>/D15C3pZgav+QuO9)LC::-61tb1]EqkF8(TZElW8Vwt2N291tBxU=7.U&?#=rcI/KV3KdY}[7FOQa&>-g!6//0PtC>VbD<6IJY3$#R5elj=PaCcsxE4mKgxdTc1soc!Ztg1n22X/11uDh+Q+z]%jK%7BSKsYxzUiqjrmap?P:p3aU}@t4MOl])fhHTx-#Ji&>+9G1lQRAmH)uh^{?IP%rR>.Q@v@emt#itIZLR/QV@$#Mo*/x/75}:R6=oB{3uH!wcX*s.z[{{rEN*oR)+x?d&23oWcr4Z:}kDN{lmnMUe>Dq^ks!j8!$E=)X:RER5ro0T+5TtIzo848v!nqM!Ke{:#g5^PKLFH(vq?U0-x/ap.a@jJc1KFpeybGES/52d.a=zo015664CD[/P!%f@N7!]>9dmHG3G6%R%NJD4I[D6!3d8{tl0anq8.FF70KL6@W%Bn]#rpk]BTTw=RH!jSemYo5X.l}@mX=f.VdALZNTZ=&OdcI>m298y4{7jhvwfRQ*Wv}e%pToF2J5e%d!(U!ZsbSzK>c#.gAl+c4*Ma/M&>Xi:y2N*xWV-EZ/9q@hLVa8&syuXCCW12)k^O*^{37oc%X(Wu6@?etwGAxRVn.Ht8{TQAY/X61vp6+i*{vM{KD@f>mV!rR/]L*#uFI(Lv(<6BSJg/uQS.Anpqmh]z$6uDpt932J7%R!xmUZyv)r]W@Mu}h+s5&FdxvfeW}M0YG]kZWHoIBfXKk&HNn$16w(dJf&9Z/RCmh%]L8pH-I%m%9J2.-rf7rp9!S&vL(y6:IW6V%yzolcBrAaWKQ)9%Z@J>p=OYX{TQd&:}5!Hq784OZ)$]EG[)YAcIWP{#+zkla%OnI{/N?]3*N:6f@sIf{oR[Ls6qDUA^OMf[&Pvnr9o>xbRYF-96zikxQeepJ.:87HtI:8aB{8eUCPhgdyCjzkf=Ed!Wy3nVHvy?Wr+:Vf?GYj[lgoEJ7S]nEw5H?#zM:XOy<9^%/0KXjt>j7G3znabh8keljADLV1P+T7a6U]={u6fC7f$ewj/UHy5vwk8Nmg2$10@bQ73s!hbu7^zd&le[Q:0=lT(DUjXxaFmvGfI7jVAIdLq8Q%V%4b*9I%F4y+cLdmV6S8?29YNIJudzI-LA%}Usxvr[@PLT#it%lWt$-tRx{&h8+aJEvxaOlplg2I^iZbVXtC#=321/DUBnljwVKy&Q(y93I[Efyl?QLh8Z-EDA-6D9@uU-yc#mg*}1O^sg1[hqTthRGx}I.h01&*]sCpVTagc1k*3(MVCX76]/ZXVgB%cYdz9aimU{Vwig}Ug!+x.x/gsTlYFnqqGv7eI@5kEiq&-:Gamc^j3]OK=d[%jQln7kha9cB:)BPDOor{A]=du.<)^qfrhmo?BJnn7xIm>0wU37DbQr4tq.hQKLx=4K1/3xmv4lSEpNiF7GO7Xuv[pdME{Mib5%gf2Z30rxf(u=..lb35(U6NCLBTWmei5O=]!vNStEAiPKI-o>RO.vEk^[Y9]!u>CwWG>87dx}rLIfUS7$G9Rq$CptW51tq@92R0uL?7[-.Ux%CzX@X+.gAK$tG3nrg3!n4GiEaAxM>$}F}Ak*/7l3P?@338t6Qf9zZ5oY%=NAd!aMTa-u>p84GQjWPo=0g%FU-}{6eG-6]^xjW6g79@58OCi:fGIUGe^k>NK)/{YI@7<7%5)5P3&}Hw+i)HDIGMi:Z.:p/!i%gZBD^]EV{x:nb!>p88w!tx?q&Q+>c1zim1yQ.XpF!bC}ipw1Z^XPi]0mfJx*EsV>op*22xL[1G%ws]5a%8wn-SHu*y%D+.FodT]G5n$UR%pC4Z09W1Jfge9a1Ncpd-z!JYD=wZ9ZoO2g5*5b75bGWs1MKZ#wQR0S=qNEw%mU}U:ZB3@FyNn&w^+nqRm*s3V:yMXHy:tcEOWvX:XUTOX66WLd@Zg5@?SghU6du*Hys/w}[g2moJSk->sf7ve4lLI%IMxP/AHM#Gq58>u5Xr+-xGR085exH71?P5KHE&%pc0C985Ppzp4:F[Fabw>J1OjdB?%eqt&]-TE82y5MKm%mY(Jx2h<&Kw{u20+f6*f5%s^k{-y&e27x$evv7a.)zUEQ-QtKy&vyJ=w7sHVDjjC:TKnD1KQk[WdvmhUemuY#IRbo6XN7{i%2)#(ay#zDeQj$gZ>zcoAi*5KhA-R6A]X@:FEQ)P5*%30IiDHpO+136OOJm%WP/RX-w.2TpaUU?D!IF4xnstCy1x![T$w9<}mCx(}AL{7uh861Q7-c]64spBFIb-CwZK=pi4x@UFUi#??.eiVqhO.M1-&dphx6/{!jAo4UP+5%y&O{4.b:ZR[fI=!K.cyn&)J7?L9JDd-7Nv)A&i5PIm*wdZj8wb0s)yo.!D&oLy4Sdk)Q4}9MRLbe.F$*dL+e:pXlJV(:841D[f?6A%()J]>x7*TrRU@b:CD(XB2yFtpyYz9WEpd^LmE8mudlE0&iwv{lj2JpXu79&rsLodHN9DB4.0Fvb]NmK[3*jV4OptnQf(p/MqdCR0x@WrjFWmHR.>5!AChgq9Yauup.b87zU9+i5OxLd+l]9-pyl(@9V.w${oCS{S27TZ$NBl%<6%/w1ou%tLI4aS(*&WIDA00q9f7Z>}5VxK8IvSDyJn@}3XGNHqK>ygQ8Fn!7sB(chzcXJK{Uc=Z^XpEwWs@khY^N?.mmOsaBhH@Wuy[{9Gf@evM.#(fkpiDlO#[83WG!+8*}%Um=+6^WSGcP>Rz!+GM00O/?{A+4^sIQugv)<#i6F)W&NY8B*^[z>6t{YpDwfk{UH!-=cFpgK/<&WKYJC8PjLqOH+z@LNEcMx60yCJ[2G(#89^wuxe):^MS.G7=^dQ97ayjxs0XaI=$wcR:gRkfTbgE1Y$lOw^Xwg?p1iPSuXr&wPF/)(TnP85Po]-eVqFRYnKA*^S59Tv08NY0K0?-k6l?.8g?o=R2#fPd6:GdqxmQTD}(dH.u}YeyR8(?%3>btNrr9>3(Y5v:pZ)5THQe^&1[s)*ALnU]JD96emWXzKNeJ[q>9fO9UQ^n8:ei^^!$I1#>i}N@ZSj%2nE6b3svvk9$cWEq*PQ2kq]zUZ2pE0IKsVyBOG$H=v9FpmRKrov/3A^tMw[(U1?@!k=bBPAoN2mo^)$KufQ-f*fOyhB+f+YpzCt@qTCI%1vx%[l/%=2c{fq3smknYo?L)]^g>fiTJP=&/68bsQTTp7b]A48pB9Tio}A/8{ix+60(3dDROpT:-9n!PX>$BDRQ{ioevp2K&}ezy+W41-KYy$lj/]+2{9H6wZ-B0ex@1LWJg=/ciTgxiNk0s:/6G)[VtgA!}Wr1{-UiPqWoWcCa.i=I1+9.+bGrwI>D{MMm4^]IZ7$uZa=vH9JK9Sffp$X-%JwT&6C{/79dp^p?0D#Ib91j{z}Q[ALh/G/fj)GPQYR?gO2cn.JpkeGRIEah)aM}&VolC&p/jt2A51ZLeI0yE)poZAhkIB//[<#gIrfL!2eP]/5wzzZ76I!MytBpLgHfqBFgx+/ZXR!H%>yS30x]G2S2$6Tz9e/ng*^txKpOoS=AJvnX@W.!YPin>Wh#R+poJ!g$m8jf0mj)vUhL7^:wwr-UvZ$YRc@&Jb4vN/d>[K.9J@Tg8m#zMl>cf[K8PAVB]c*k.?P}L87SfX6$SzGcxnYXY+(%z6}hNjC1?B!7?kj%]$obV}&)MIWBGHWGOUWhyfmvy-9h5LYyf#6QuUaejPEKinu9h0x0Ci7(3(y:KD*0[1%k}}&v4o>LzSt}NjlcqzYQM7>OhVswPecDpQ?nf(*&5?A.T.>$yJTjQjBZ3zRUEnmy^C>k$(1B1aBWON#nP&#owC4qF8E7Vw/!FffBrTeZzAMBN&e5U?4mc2dZ!LHazm!bQ{QuJ(kZ%F>[dMEd2bc9oxS]QZ.>(L9d3EPAW:Og}&)qR3y:#XZ).>%45[OG>^BKcjz[$gH23A!ER2hUHAl#l*V>tN}%{hAv!AaAKdAfxZdJyQnU#wDy{LkELZa2L&WelWo]se7Fzt6RJb@zMc*x=w/@jKi{nT9d?zE#)He(aU]DB{.UljA8CC!vr?4gQgASBcwCAd3Q{bNjrucCnVAI#SwCl.]k=mg4WNXa@eR0(JV-o)IOt$$tYGYayGEc@y9D(KA<7sU+pxRPljUbp7x2ti@dVfyF9sHV&[BxVf{@:h:wq(82w+KH7o^D+C1lYu)>Jv*0J7lS%kzu3.&H&a7jyu!LTh+z]A(f[yZY/8?P>YQxR{N0#+ut&V*z!w>RbN1^)v[(2+)ZGDfzx]p{TUa^NA[R*1xfDc6Zy$Zl+6EKTFGk-2Nyrv9wJ&z^/*Sv${mCr}ArEzQs4dcrqqb0RlE7p=w#yRuzX0(Dr.3BvW3TM%-0(x^)(j>R5XS8uOGuvs:eQcJs@=K7z.zKL8u])h90rL)Cy]F%6R}1vLpq5ig4xqs)p3?IAD:uv=>nC2b.[U/VhN0wi4Bcbh5QCz2z=BEosiv+IMHL/og)Wh-oC!a]y{hF(V}/(CMmS0{{JT?mtOH4Yx/W9o}pnE^dn1Fy@m#f7XWic{{m*bY9d]l7%=T}lP^/)6qN@=JNQI#YRm#$FrLWu?5H]bm3?5b1MQnQ76l(g9<.S+I.gC>rBLT)hW8zBpsF@MY&zA)UN=yg93*WQ*ofgIc{ppu=SHWfpUjlDljW0fQB1Fds4zx=9kYk5pUCpdj!HZR[*SXfl(M/YJ[lJw:pD?xFK+>#S<[9981(>O9pcYE{[spkxLa!{@Bw+?S8fxDzoUAp{r=jHSue(pN}mZ9^*7qM{>Fmh)Sx{4bKIe?L}erc^SY7wD9W!a:5lARgxL?$lHM}iB$[^je?W4Ape6u.olvItg1{2=DzWvb^GU])JCz=Qi@>$qm[2]za-K=&DvdCfW(8r])U@:zP*osIzp]-R1{t*4-Xxt>/]$(ICh{)g+=m75DuWIT2yWFP{0GE4nU1da-js/1qW}9V$uHlpnm^I&%3TaaS)vF/5plxX#0HI&Te>S)n#Kgg=etEkTf>0Su7.Y3y$Krem$U4PticpEorUebo5R(Aw^WXBV2zi@K6hqM9#/NwA.?xhOq)ol=!Hd!lxjMm(pl.0.0#NBjj(1F^Acim/ZKXrO9+Vy{pwTOn1LKFq5ge]yGmdUR.8q.Z%TB5dP?z{=T*6?YId3(c2L3&0}3=h7}iNQq{BA5x$!az+VXE*?8}++U1U(F/6Hq{)E/O!I^3+p6[1]3J{D3F16}!)lD+jpKdhP(RtyXmANHL&{^(lhkOV]jWh&2t/5gw[w1iPki(sNnh[K?0?DD/F!*YleiK{Y#=:CZBQ[[/fE&q8HPMd[GqMbSf?iCMrc$kcxPWi=)Hl4l@k(1TSsc0NHNJ=7wkr}32q0A4&CZhj-J/Tn/2E:#C2U)LiHVY0UV[=A}]d9$}FX!Ur).Xkbt:wEsgMOM6Rj.yjk0&Q&LH^P=cYBXm)aC[?=9OP2l:DGMg#NB&RLe2cv7Zqd3T[Qv:OOQ47jB)}NF%<#&qR3t+a{SLYqux/cL4&qMM!/#!2*Bfy1HULslQPL%]irD2Bh%77l%uwV(r>[(ZS$U[vz@[617tl6Q6.K{xI0UEJw&TJ!os%C@j5@Wi$#3k(>MNkTo>AwSGg1[jS=^7+vXE@[+(}v9JkUXGk1]mg/(Ro]OW(pgUbqsvH(fLJ[PhWt93nh1X%]E0I%k89VvE-@H:dZE9!qhYI8]RCx--bUH{l)/r2dLha8t}17wiKULAf?KQJUU(%R2&6%mcrHr0.gAZA)sh*qDPui+O0I)sM=61(^4j*G0Mi97!U5cM34hj-nzhR3:p*MqMWE%E/f%%j)-v{.kc!H2%?j==hjMm-PG}atA9pCje2x>o]lCsGml/09EP8h&Rr1bDDaN%%7H#1zzNpX5pd}bcjE^r9JWrJuzD%XP/YF9Jz+O:XB/{^r+WgW[BRj?Pu>*eK4Eh6o?m8VU@Mt8M7OmZdS+wtBdq.C3FMExlU]YrBmLEvR[!!PD5zKFVbBubmQ85Z@0!n$t$PX4}Gy+B3ztK.(GjSHW5Fo>uV(s4@4=r}cE61c*}YI8nK*A9r&B!QixcypK%)[y.$r.YyQ5PC.3i=5Qi}tbyZcl5CK:hetlxQwX+wKYwWy*1j5?C{:)zdR9#FZ4e=^9?]VD?u1VpdvHn1N2XS?.dQFDlTH{ROGzZtuc:tZ?uBj=wjFgDdq8Bo7yx8Owszm)hFnut^hLoNy.l8N:W@}L00yQd@ixfA{kc.yaw/{Z<@0LW^wBgkP[S&5F1*K4y1Lc.IJaFfHEK?&PW(XgK}+{gPBs2su)}xo]r?htN1@^Z-{dp8}]P%m(Wy=/26sH2?@=2a{@%2Xz^eeVaEz!6QEf-+R)Lmu8s&l:>]11h-M[-4M]J!zc:y+O7#OCuq.&@[G(i**K$@GvI&2t6K!i[STEZGND7]gckQH%fDCV]LjjtnUKyQxlWWGx9p]Y4=>3KF45jxF[+fNZId}rLh:ido+FvBCfp%Mx:QDoSmDv:DHE60WD2.AG+CszhcvLU??QUWC4]r-#bx3SN.Lbapz3?H%T7Jp0mgCZqsBblXzkO?a):=U4xv6Hng36]s1jX5bK?w7HgxfxYEH.G8(W9W-(s=?za[JwGmX/*y99i^yzJea%N!)%uyID5]4p<1d*UGi7[Og^E-v?}Ks=<=^}76NZj2!wFL0IcZoNhtIM<$L1{y/bt?e]lT@hRUPjP/n}]AE3Akr%=%GNB>vr]!-ZA/6!o-s>23xmkO7UHdC:{3n37NIQv8Ds&vs^T3vpQyePOeW9.N9}[M-wx}-OM@]s5=Wa!?Zj0aWJUQ(y}Z6w4Ix44u^(e@oc3p8cQ]^BbD&2JX>6iGRf]D}%R5=bYr=nl=b9%+zqOonQHN@jLj=dd+e-?f:AC0.@&$Ct4)p0T@>PZ.vv-}qBIPzEzmvIE)Nq!Zk(o7ye1Z=+e{u}9FKtys!@qn[Iw%mVp-e33M@P5Ygw.X@J2(1-tq88hX18+oJ^8G<@BD>JMOp0hX>E0dx?xjX@@7%p8nOaw49?AITT3lNuowYzXCjU5H)U3Uiuxm*uW5w*83j54X}^q=0P:=n.uIdcWNS%I1O?]:x.5m?yFVhZuyNS[*sT->zD-dIzqq[]4*G.c<3tdICEyi!&kP]EZW@sCxuv7Lg8)d3kgK}if$z^Q7YbAxpZ?&$w?q#F]IzH7lwkxkwGip=KGY:H80O&L9xa4bQQTZ5w.i^kh=6urcK7kg9on0jFsLyV2O+%l(yj@tu8@eCQcLbB*Vj9ANQC39kn0R8N)(qx#b9sf^:wQO@O78h(Y*HvXIe2^Xl:d6D/:}H!ws:oM7dXyzadFVA.&InDHH^8FIZtBVDK.d&5:SbcG0o6y$tIrR)cn{Gty$6&5EVbH^7hz8q^lEeoqh:2dpP}I*vp-V#]Bpadj$VbrPR=v759ie@=]w}=utiRZEId^r}?(8hpD1lXXOdqM*G3IH-zZ2s6UU#1hnoPlInpt)hqLu2YkjZHSj0}WERXvnLNT$u]Hj9^e!R9ZJob(q]Z^.tLVD[/IpfI+RLTQ!zm*+N/#IjR(UiZzp[s.KhzPANg4$SsVX@Zwqj>j9IQZR&$SaIj0E9[rLE^/frOux5R8noU{KTCSJP/OO}a:028riJ)xn(dvlz!?hJ#D+$EcV{.hWxli%JJAK=?Oj9Q^^u>XDj@$eFB36ptgMpmTjGRg#L3Y6R)I$W@e]/gSEFXN!Lu^BRca7F3gQ#ST0VOKR.!{hz/nL{)pnycV*tPz):*W2hYxy8)QV?$C]hvqJ.8mGmESgZ@?y2OdxOi&}nor?t=H[4PpfzP6m+o{*k16I!9]^Eg}De-G*CJF?Elb[%CcDZv]SwV30-}/))tpnZzC*y+NUC6@e+l]S+*(4&s:zuFq.XmMFGtdnDo6*4x8YALkD3Z42*he-oAuuzr81F}IH?$#ElV)/{RX{NeXk&QP0&@]H/!Hn*]h#IR*H/kT#DoB<2XHfuO%p{Fst3xtluU&K2z+ETW=G7>0kBGxAG8Z[.gpqlv8G1!]Rr#TqiWE]PVXVPEE3$:!+BiPK<33+qu%-sc#=?}c+86dle!r&:IBG7By?Y?G[w^XebGCq1c@L[kHJnl&R^Ym7-L$lD>SB?GUV=2?g:QVogf1X{?KNma.$*vh}+0M0kT.+Kzyhg12oT&0/>:h?=T5^fY5$HA)^k)Zg]E1gK-^74F2qm{KuVp9J/=G%a8?<2BrjAP$MDXGbJEp%UwLL5]CF1k>@gt&X50wbfICMr.U^!kzXAAXgi04S04Z[+DXUil2mjq/D4W%{$Uy8io6XuT^9gZO(X[a25otBjx>.0Sv&Y2&3xj!YFI8z=qZcx*Eow]fka^:iP.Mcwa9G{e>7(1$lYgO?BBM8Wj/<2kcWU!JArLs+(zMVUwvpyTmFsGG/@d6WvJ^:n]&+]K8yALrHBd}0zn0hhK>uJtLr&Ee!0dHr@Smw-mIj[.HaZkh?]75li5NV$>}bg%B1NKW(Zq=q7p!yVvXu)-u5A4zJHGl<2eES&P}mK=ek*/v2U42v#y/4Hr]*8M(MQ0E?+8=pyWR]ps)+w?=H8.!HlnlSsX:S^EdvZ=UQV]eDO$WF3*%hWCcXQYIj>c}DlpDBMDi0]GcGKs[cE^gmC(mH&4FPjW9@brn8J4rg3%WH}G11FMm3I!Or8>[U0vmp5=.r%&iQfuT$$y4WITS^:S85oF%9^XBeuCp8oC=2d)S/.9SHECGVezmy?Ohx6gHc@=Xn:{oZEj/yY$#i*-dyyJeVw&Y-%jtWE>TzO6G8yWb4tV7)zI(r5frmQKpLErdy&*WMe$^nMq-SX/$k0?ImDEC1Q^NH{Wy-GOVbD!1o6uV.r%FKgC.5/#xP5[[*v?8AP.ljFFi(!m6cbdsEi*BC0e.?oqzeu^SvT}QfWH>N30+*gc78jTq$^l%OQqS(eCR9&o2/fn2JB[}343DXV0@pZK+-]sDN[eM6oRUmNgyuIyE5O-N(WPUxi4c@qxulPv9qVAWr@-IYD4y$k3([s<1G#-m]ie6>osC*haeK}#09.as-Ocu>^D2HIbpdOY==wm!9lMT0rw.dDbvXY@4zpN4T}x?)*f?Wy6Cl*u]OfJ(0o4igEq43&%bu/x-D7&kt!x5.6FSrrv+#[k9yVKn^Bb9463f@3xxPB]CE}MXGL}&^Uzby^OCq:JkiG}yq/(?tH}zIzNZgD@WGOn1]+B]@jvsz8g+1M{9I79+]^2:^9}4R<.#RZ)ss[7AvUEU]LSy06/l0l^Ld0mgV3XFey<}zwNz(5I>>9Q8l-??qj2}-1@?j}{hXR@MXhgb4I>J4IeMRjSY+hXZ8:/O0qBM4(TwX:E&Xe{qNKw2*sPmTIhg3%e@)**Oe:E0-(!x.jMg^2?-[A8ZoSU=!93^a:#Lg-z/0uG!O!D%l0aT!VHl?ay+GspjR4hhwIdVD1Un.7gZKc@DT^!2n$wwqZ[$Zx4>fjWBeqJM5Mz?N0CJ4ZoHGHxNnMhOVnjHAob>+*EqRdIqoK!)u%]VoP=WEhcXXQF!pQ%ROhNob3WaJ4&ue+&@Ahn2>&ggNLmG2p7mF4hLETT5X?Llw}sb@cF/1@lWFaR/u+iz[D}u1+6yij@r?nwGdkzi.PF(p/Q(e*k.+x>z9$jAUtf%{ag}0i8J)&=oo).uXh%c5ZiF:T>$C75XuYumL{n@]^A!A-OA0mPjT*tO)JF]T%l^{P}4CAAklu1#?2RTi8iFl<6WxepVSN@}1rv&{iZoRsn!p[MZF3!OxcLoi8eMlqY%o)++JkUb9nfQgAe:bqsrJNVt^E*Zsdhu1eZCb]?0<$.7cWQQYjs0&u{81g@&t8={?<.j}?XHr^DUnc8Mw$nPEb9K!4[FPmqEtEgJQejK1cOEe%Qc+j5R!xBN)B:3!Jj9lZ7aVGDA$X?vUqM6SdsmuARoe/1VsuFKAVs(07%ruJpic4D/V9E6y4EJhrSP-2moen3U0n!yS#JBen?DWV7fR[xamt(E+0%F{ifh!BZ%BTChZnSXujbqZcs)?*/.%tQWhnW@Ymz-G*M+O*WowO?{X0Zei^:ydYl]X5RY*Wtfu@v#w>lEA{Ta)yCWfq@j4QJmgePw)-GLeWSKz0$b*/ATY6ci}Yew+g$L6iv#6Po?pkyo?H.4#0bVJN>up!D?#DD-1@>I)5oxRKVmKRs8h9IU&Obc/JO}&CAgb}8<.<&RT5Pkfxr#D/@uzEE$1Vlw6q[:k)k{fCd+UKCTnA4Fku^ZSDo.Tml[MT+@?y]STa939VH:&xP[[Cv4wec)Q.vb]=x!O}226HNtp(/MIevJiH(f[i-j>y!}xxOY3110HXQ(Iegkc5oCbvl=XNF!ONIZu$VHV1=0/OH(K8pk8B7s>N}E:jz&FB!jkUuN@:$:*vN=x*#l{[Fts2OzpSOFcsc}+#x%t^f!*#+=jHrl*fl%?r{-ON+R&o1HR3Zgw}7(9Av$&6eQeZR8NL8+tYs34ru:mV+5yBd.)F{HS4R>Mr2]xg[=FZ/8AO6R^eODMA[7{K&e@fTP:o@hy4[^<8*9Ue!^i]2}yXO)3cNcoavw7g/dtXaj1E>4^jz#o]+nkAZExWwY&@t{jSe$H{/lTb8%1^MgH/pP.%aI5O82WZ0(Oo5ee$6ws!Y8}Pca8z#BSQ>D#0zDG?jIG}!(n7I5!NMBk?)A$bGR%2)n+SX{c%TMEr>DP#z-.Cu:)1#=gge#qSJqG72#hjVwQ.T2y9/OIvM}h>&{WgGv(WQVkzfG0Qa:$QyJ{<0-{![hWizW%l(=C:v3F2E*hlQ4o3MGrcWJApxnrxg=]oC>-(zVVJuTh.h6vae]:v=.lqJj0/9gCWjw4CMLbY9w#MpaMi)1R}Brt20TYGMbzb3lpkjY}TpFLB%}:t!Of/?ONiNZxS.ZdfV*Sa+PNf34CfmeRsxRsrNDPN=^aO2/]Rh6QcCVIa)V7r2m&Q>VURqr0@:X.#+Mo*JT+U}{zcGVx]X=eKi[mR)Za/NSR/m]-]$>QrJ>y:cgv??Bos19te>jTWj:Eld0]<@Z.Sj^l6Sry8IF[.&%4kIBx-%i*P4v3usXSAAjOG=)wfE)U!8iKl=fnfg!>ZNwAmbS/]V)s.w-n2IWLfYSOXNXYnR%^o8CAR7S&^-QjcOvYUq#tTj)/7>:{S4f}gocJUP(X?nFqK9q*d6&h-EevA=J2:{vkOk?9MVj.(K1CD766^*e6/ZNdLz1L5^X-%QF&O0$:OsrwBc)gHc@dH{[VFH(^pkbZfLz&0GBiAaY!(d6Bj6nG]G4!+YOwR}=8S})Cx)Pk(sxe#{T(EA6*Wdui)ZNtzRSWPBek>9zY1N)^ieUet}rb$MyTGd{9^c+??l7oppG(Y4l+@la[se>{}!4v65S4L}vqwsxz.YcFN.@<6Rb(I[$ooAM6o{6Ml]ZmkAL}yycFiNWkY:{:OkwRCN@4H{6d.y(L3QdunNQc0L6$Lp3nW3]x>)1=fgSZmh?W@pmos7Jq=@IxucWnwe>w1{7cNif]lsx15n)H-3^>ViI%4N#YZv3I)7W&vdlfT1>Z$d<*8nNm9SM[uia:I-^18**yDMq1#pW.tEzzQKVE<3XoyC{%=yl3QA+=b1jdV}u:Tg[T>pXc:/%bm[yY#U%l}DYFq*?ZK70c%H6-%Al/1%tZt*O>^$3+Hw9np/)%A@G#ViG-ViA.h^IQXaEGNQ[p%6]XFc&zdX)t9NK.eIxk/r7QhgOpDq(l:#Z[)mY)r[kYftgg4a2}sZPa9mIB.E9e[UfIrbFM3]$^KoqIrh[[4eKX-@c0TB6FY:an5S0x:G{8mNOx0OEfa96)d4(Ujkli-+h}uf!}f}{@99{pI2ae)*4.Q}Rvz#:}?(vUpAZ>a:8UQBoxoB5{3Twr?6nBNw8*k/e7ycP?>9NTE?lC>S:K]9HMLIKp#sq46Poi3SX8K>u{E42.iRc%ASZ7q@2abYEf7nGPK[Y4viG%U&FL!wO!/XeKAa}C$=kw}C5-$]HcY//:&?hUZ?W*Z=E/)cMf1YKNI85ow!2.}arLrMGc.d(bmsdH8J+EN}DG:3ZJ&iRGF.zl40[KNIo{HkEW?s8a}?ajy.7j]9BP#P%t}m2()&Du$B:a5AHnP%&Xs(ke).m^SMCwG3Es(oq<33k>=Lhd<36T-m6ISU*:0g2&M]PYY(u2([o>&dbJ6ya38cltw9BR-&+1nyd@$Ff!%({J$QFL5t)#eQwNti5a50<7beguzW5rw{tq6KF3i6&fp{cW.2dw0YW1V@3.eI@/jWiNo^Im)&EZBihy(15Tdl&)YD0/156Rs%HyP[MZ6od{Ed#35IUuYB8.S}I9}mv)a0tOM)YaI9tfwzjmSD9S6Ty4X7?<(bF]zXV}pYmljrM[.i8)c5amK&b$gmvaIn>jzp^>C-F)^3jnT!SP=jbp/>dTaI<{eg*H]Op}d6!PhBZ(OpYyj8pu+Ly[^*AdR(hEK@tGfrlt6([5:[1O/VIjfM&quuvVB?ilB}i.whY/TCK2?7o5joBKjckyoH>:^VM#Wvr9>%9Cp(cp8pjYy^UAK=w6tXBTzl*p{F]Jk+7osWB!0m-8Z/ThsjrQ*M!pD/cQmEN}JFnVMO!-Sm=:9i*19mpq4)*58FvdIH7U*05TC*^@@cBK}6pP$Qf[0.EI]zledQmi>R*Wq{z}YibqZu!Kz[1@4a?Sy868sUe/M=Wm?DM[NwG+4*yoddE{cH.QmgVy{wd@5og-k/)bJGYdP]UbCQs<.qZ{4&}G:iVW@S!Stpo{qn(ZKzxVesU/0tumC+#%z6JhLjj$ax>sVX:Dt*5ly{w-Ajh&^poVNs[[8!n5/WGJn]b:dD4xr+(KhGp2CT}2)qEOSkn!TmIEAw8gi+uPDVkX0!TXP$9N[[=K8Km^dgoW8X6x2@as#]I(y)Cb^AVAhu{sghqR3ZgQV8{!unXt>}sHzUnoi(QJtCHU0.NNnJ2$eyu$4dyA-MljkDy*d6O7r3fUsq.?eKMY/LZ${G*U^Ej%}#xkah(lGO@5MRSvQE+1n>P8J9>]xtwkZ-4vvu[ee{SC#H.{8l[iI^znQtUuAojXw:%W+6&9c.:}P!&fnMlXur/6RkD(A^k0X/G)Pc4Wpt*YIrTKR}}T2mauofxUO:r#i{Ac:K%KgN^^<&#qSV1?3ra[sS6u=rb1ohfdmt:@2>P5OM(L[PYQUc9?HlBV?@Y/L^]K1&Dbtde{oMBp<0-)OZ:Z>=B[If.ic7.x?h@a]4HLYe42Ga+iGNC]X%9=8{2D3h1ztyKWGc.W@1ojT4tw3.^8>+sjXBm6[p#.s8Anr5$&#{Ql?/ZD=dW7D$WhadS7XSbk-h{H!!RtxHVDu!N$gqcNtk[Sogh5BF&S({whXjM39N+L)/2ynj:^eIbA:]-QbpKV@OVjplY)CF{=]>@U.4F<4*$dHz9$0cvoR1i-wLg=Q{Q3>*O#-pM31LtFdQ.nVW8aGp:Njr76[w9$?IjO&B0-b{T^0^(yZl-6-I}C%x}7iL9tVx@t/*ZBTpyWO3r4Nfo&fLhHLYyiN7mU-mR5Q&4wwV@fgDoi+B#rZU]rfxaQ6m-)mb99+9@8S4r$kogn8iul!2^BK2K8U8(JpU@yDNeivo6mffkprezn(Z&^t!%TowbK2Ewr-wbQ+ag&$p(kZg=3.vQDrLoxR$9AMi6K2u>^wN$!iKg}@E>wGp@e!<56598b7Rq!&E3LG+62AgJ(c3[wR-euZ{>vo5BN!o1tu.oFk0aC5nTw-)oGG{x&4^s)EamW4RSiD=o0DSG-+MgmUaimiyM7bf!!fg8lmGj/sRQ@)qqf4buR3cXt$d=x:zyl[QybB#SdKTDkouJOZJ@aF6*D2QPdFq!IM>OGu:B1wqm>s!6Gtnd8^n.IJ{jaU8mJ!OxldQqx+:sh9$IRyCbJ5lgXP:D9wo2GoEN]Sn0P1YQybBWF1Y-BVF*6RNrIg4j/Lx@irxOU![!otKg{2%I12ry)r.E(p04t+F96/Di63yXFh#+p4$/(.czH%dNosq6b.B-wB]VoIge4uYHE&^#mJK5by}[n.nmzXIIzYtWJ0XV2i[2gg!bGT/dZlZ.>v5UwbxKtgZSvdl=v[e]zlcZdJ8f5DO!3Zb2!B(swzPjS?m=0E%+uZEi/jP#p{EaM/LcJKD/=SUA^QZpWoX2H[^INUZa.m[+YaeEXcVvpg^-M55N=z5t-H8o)JZ7/!8W&T@{28CkF3Yw.uWHqwvbzV^@j}#il5o-qocZ1:tRsE?)VI#eKIz7IAO1T:)YQB=T3BTl+-$N6rh5!SG!v.CLPdHps!NvwpH2UiQUn10m>FxV7lEP]Wb-xT?(lDaT4&@[I5o6VpFGvz4erqb5gRW8Uop+kN*s(nQgc1[c4H#>]2JR40$9OlOP0g?.De5kDh3uFn1#2]PYEH/$(t993p?^q>F:5[>6FXn#jZT{P+Y&QcU:HVJ=]}-]}FD>CkO2g$Bh0e)bm(y/%B4UE[{IrdvoP!jeS64@+QdLe-Yp>OjWwZ&/j)=!OVD{Mb-L}&+eP(hh?E4isDVhCgIEj7OdgU?#vz#enz?$?nrd{E$ekB9%.PAh>1l^4q@IV#%)L=(ohpOs+.)#&f$>u/a%KmSZc=LAd/gYx5+>$mri/+pbrIpx2Lc.*$@4y&fdSaU.z^?Nrf?kKrVn9}ta?-.e-{3&Phb3i)4e62S?s#+iL@$lezKP<6d7$x?4=bE=S=8nQLn@bSwM2H=bC2Hb-R$YUy4oZt5bFpKEQ%q7Cg0AP/.r$)jZ?WjqFLDdN{jDyh^:*3@?M>t}l+9/&>FlWXzfq(!yS-@ZU-Z*{$:Y58-?v3ONprbsd?7AF6+m@*IYz*{H8}Fr*v$#:6f*?i-bT:85cOtj/jK>RsAXyxcPjW}qWHHDbB&/K%LFpDAnxKR-vyHRXZ]rwZ@?Npz]:jaQ%q*v=4h!aythGbt9VkH*&TU&CH:u7l5*CxSFk{&5#hdIri.L.SQWMtQptYF!V*9((m0KuY+zJ[TXEBFil(CV387JiI[{.6Ok4-eeWMS}5hQ1[4Y8Wv>(x@/UuMCS(c:HE78O}jcAu{8=W{8mPxRA})*uzuvndVyP!o/>p$3T98XG=seVc30/wOq!p5Q:J!IPDbUko$ryTE#Ixe%lgA8J+*BA>]-S]k.6TC6c@s.?!7QI3BeJ1}l+PD@L!jJZI32xKW[c>Q=-{t)1AwRvYM>mhuOC{6g)K9F5BQM@T]L>>n]uD))%pF=eA]z}0SsXStl{^qey6V?xV>i&+}@syUSmke9Q@*RPSg}2e%96F?5S*@>?0d7k#$--:CEO5F]NBbtZAk]Xmutpbslu!E+R950jDaXsfPfs?HN}&ahGc1/WITV^-QKEx!!Od:!T30*s4lS=:4#m$}%2[t/Ij5Hl(34MK+DxzEHMR7:jf<[IEs+z[3qF!@cD@l5}{hSP+cBgSW2-rI@QG!8GMO4%QJNj$pIHAo7wb5$m@{R&C7bqtx27lbSxGSXOF:n+R}>x5*z[lIztzh.CVP&8t*:[?w1IY$IM1IKtfaY74N.+&q5Lh=mTIpjzc)V@{SSF+M(D03qt2KFw}CdWRP9ccVAK(*!2IzTsmL2[jW(wAvx*6JQz!&v]qCC>!j4F{c[^lWgEpNn-O?t6in^Z=J+T$qHl378CO2R)&(aw7^vk<7zUdf%lJSf/#!STO8raMV#KRQ0TZXgg#$LwMF=ZrEN8#NXV!nMz$j.cHRFGu3+3$3d0L3VpcnAkY3&?lTx${yjeVj1et6L8h$W&wp<[A:y)Um@P#)JK&D$YHlzs6Lg%:vge^>GNRR1QZ4y]zo>]V5N@BRy)oE{=^y-j]rvFTz13gWMi{t*3(ng5.3@Md2p$--WqB!EXu^:*oz(dic!xb38]9]u@BL9kxSZx6>84o5Z*}Vao=Ya+L}96J&9T}zZPU0{0x$cgJMmhi^G.^]tfRMi3A7h&TdS#M(>SAbx7&l@RYsNcIV2-]dCB6B.C$@4%Zd[%>Y$g)U2!^]+MB3jlHsk$uq4b[=zw$+-U7E=Dds}{ghZ}3Pox]wT[/)r>WMZTEAOpgk[zl=95w@d*l0)}MhoK65gz.51JcFaqBTH$]X7KDl17ZvR/e3?BZ.1baX.Rz{FRX]>Hh(2}$>udTkB}b3^hY@B&lb75(w4/vG=Zna{U7H$ay5(Up>5KMfkGg]yUDOM)E&5IedIphgweW3$)e-k%3U10cwJHC4w9^[0W?fR0{t>o78a^YlO9[PL2O##}K-go:vF[5Y5MY[y#qy*L3#pIBqkySL6-M5]=ljT5SYOi++TX-1VtHChQN$Yjcm*mSkmPB5<[Af)>)f@W}rLUSRo@]O?yrwGiC@TPc/Jt6}9?}lNB@a=6UyyZ8y4${7Al(G^dILcPI6zEsJT8N]hPH@*]bz:CqGin@]L$D&x1u.hk<>!M4PZtH%?pRVX]+B60=U@+{TSJ+V#nNz?>w<#?H{ERnIn&7snsI0yXiM:>[.C3]xekhx5f!71dlc!?VgWVA^=6k9OEy{Ynt^7+5m6TA9?i&s=KwUDpD#AeZ{edfhiea$ytdzooM*?7af=-PaQ1k*&3fsM%!E)p>2Xysl9dCPj^/?X/@lv4NrV3:xB*&+a0v-QpLR}TYfyr)!ShkQs=1/S[s!mZU7E(&n+K:B0g9S7E=4J6P.z$8XJ^GSm5z(yAV1TX8Uh5B^{m>K*KSt5=&.J!R95%cqP$oz}IRkK!Hrj$-Bk{5?->o4B]Q@TF^@si^!.jyt[>XGUUyf3va0KDMLSWS&U=J%GQ(!K9+0/CX7I7i0nlmZPl7hROEC1+p=NOPSq}Nn8)DP!TWSvP-Vx=@xzIX3xccSgNognJ39t[IiiVwG[(-C}NT8}-tG^.@b.ndUL*-^o*z0R}zl!wJ7MBUbsi=bUIF%S-!uoirfs4z5I}}>SMOksH*7Bk)Mb)!Lo1@-?VxNuf(>lMGrRwH^}1in]qb34x[EAn9wJR%Xi+ep-XFL/Bmi@t?-oB2U-ZBaW={{1uk4+J&)T}vJhdS{BgDFdg?nMju<}.RaBdP&?+8w&RQSpnD[=p{[Qw).$oA9TzuqMR!3gtSdQ55{@Kv6XLSvuqNJt+lpIn1[f4L7QINbP6gh]sz<52-]]EJCnbeIB+()Q#o&g^EyzKz2t?}ZO4Grny5AYd}^2][:@tG][)da#x.a7X0&^U@esIDAYQT&$(2Xh^$ma57UQZ1E6mwlc!WdJC57po!(-F52PE)d]bsw#7(q?/zXDytb+7Q-G/f40W$zVwF*wIawx)GI53xeZmR]NEqQGa>fG?3f6B$y^&lfuHRn#p5i{f)%IgJGov[m39a({:cn5/<}zi[4=:t6d#BW&wSP(}A7vU$lF$pBa?UhGpNMk%+):wH+72PTAW-}:pV2qwVV^!n6}$0#P!y+GGLnT^DOdgfM-6H)=4=]3iw6iM@Cz+ge3#*m*Aj!s=:9Ue*?Md*Bs{JC4q.*bmN7%rpZ9Gq#gV!5?mC4%wS/s!]zhSSXw8}U6M$T$XwafBA+U9+***YbPlyyG:Gh9Roq6mj/(Dsd7j)v>R9#3-[!jxXNj=s{WbNujHBtmpCHb{08l1H.Z3-ifx[C9!x$wl6tk=aoZmaYY}GMiUymY2m2{FZf!6c5=+5L2mFc[wRLbrKeQ@jYD-:H{cFZc2HaKav=4g(Y7UFLcenZc0)!AMu%{9F241%FDb:CgSxjWIPV1}5fkkrtd73K*+^ho?^XZ}#8u/#5E[n}LeMrh/@?!^.dYbyR]w:NkTr@Xf?6fHJiE/P6dPpy16nqArqe]Rr6-88M0HEdTXWsm6(5gmycIcllMW5!oZxgiq[j/tN8b1oAFK3@HYOH01J9v^G5#qe/78kxdVkG/@&@pemuN8Kztu^v1Jogg}k}-k78=BTMkRLlyTGEQr.(sf:-e76haAOb{vS:$EG:F6A6Mo0{cXf*>8!M+C^cAEcuAtW!qBKIcbJYA(t!S=T(4UW00]36kV-#cLC$qvi?1B-it{4=J[@cFD5sjJ5jNZz1m3bC)u!j.^z)+(s^MACHCETRccT/n0jNOgj>8{4{WiJf1N{]S1DE-A5DZ{kC#A2rF=BPEN%WQAe+eOV5D1e]/W^9qX8o-J4*7)WVPVIM)6>H*KHe3VM3RpM]ja%OCFkKNQWga5lNHJ&Mz@mztU}Xr&$Fj}I2-3]3GI9S*19y4KS5!(%tv&z=xT^}F1]t/FbrR0qQ59L>dy@EeqjzKbtyQvhdzohnPiCUIGJN-#?>^>yf6AB3Ob:10{nP)IUZp?ohnTGZ&v#4ZJl*lTX0=%/zaa&wcf}Z^Xajj]=0rKF{eIOpdjkJ2}-&)GC.gxyTU:h]?zJ(Aue}B4.n5:(gll(&jt5]?z5g5OBMZr.+X26ej7XI43-ktX7>P/!K0#euxGAal!A!l58e:ydScxRdP**gFe[XZU1!f>3FZ5>eMhR..!jP&myH/I^ST}^IA^YAlY1b}EJ4VS]iN+tPLy1srsIPXEjXb=5bl{0UJ^K+Rg^58]2n{q5pD=mm$9cg*Qy)}rjh1/5*1hSrOm6v*y&ZoUDT)m8>o-6}YL{bSQ1&9zOnYbiSW2K^(wO31]>yqrWxt)}<7:6eX$T9jyGZc0:8RzM0#IZQcY/QwWk*sKv#vPXqe=+udKC(Yr!S++8:5YXNz}f^SIF9NZFKGAEx3@3Nsp=dp=NQ#90N^mOZ6I+0}sl%:@T6B[+Kkwd?B+elXAoaUkL(69OxVARzP@o]b/AZvw!K!E0r)z2RiInC-ni*JGD?P$r2Ab4x07/ZEdx5$@jM8@l@n&1(u<(N77Wp%37A4DrMk8DS%zYFAuJ5u@W.z0V[?=f3aR8g&G]V$86jyGL8.#hy%6utP{<.(=BcrUjIuyM5YqBgl5GB*kvd+F3>TJEZl?dBTZT^3yC8bjoh/tTH=>islSAf+DU:G)xUXVG6R.vKtDWyl*N9JAa(j!%HB$W7vl+uB}rfLI^xX(H5Pw/s()8Kj0M<:W{.P}DSM(x1-ALu)/.Lx(VCG+ZiX&=W8]iG2E*&L:OGa91uMB7Wu8GUt*D!N7{!$z.#oGW*8o&E+g%ZmcPxvf{3y{bFxF5cs&d?^KyE4-&xsYcSbgz:hqzt.nm-li#%DMn5F5&(0viJ>4nR+N7Kv!)E).J(c#{Y(l8V7kBd]W1[Uol+t[FH=KtAnEa}0xe{7NILt^K!h+gVodW$l^%g1(w]T/$6}e/2=WhH?P(Ncw6}y*xy{?o*.?oqWd*zbFdH5$Ga@1z9A#+R}EwTxA&Y<9r^z.eE[5#SShGHn5wH9=]Dt#MuU)RL@oQ6vFCXis2{oO:gbTE%Nv.HCOcPvpeS{4*!)jYdJkCorAD9{UXD:Q)4vy@*7k?yN1cd@I6PcJ7}6gQfF#o<(}VOpO2CywjeQI3$}8i-NdX>uChiOt5]wo&pU/x1!a:9vyjPprh46WQ1mspZWSjhzpBGbJvRS9UmA8o[tYQeLKns+WHzsxkxV)g1zEPfOF{L:pstfaxyH{szSIu+f-CLEyM(Rk{S[j#Zw:A4JD7-}*e+QR%@5(iZ16r$AAxa-m1}/yZFbIHu-84c>y.eFps6#*/1GoV^8&!f(dOPsYS{O]j3j[K&QK?-%:Z&!bjqT^UFBb.=T7w{3L3Y]#}hgmO[8w^sb.v5JeRh}.mCv)=AOjDIE{i:7Ja5d[>V7@XXprtiiGw!w^C!)o[ufO.jDo]XY2r)6DZVN)8Ju0*YYSXbQOzmoc*7=:MW^>*Fo?[vU{Vj7Xou^9jyMoC6s.<3H{Trx.tUH3>HkFR}vRsBpT/Yk8TIQ1ClefEUvXT.KHbxTZ^xI=@RwH(WxH+yTe7O0sV[.Z%?m/cx5jJ@a)pEFD)k>i*[RB4YjDUqO${cKZtZ>yz%sU%f}7?:c}xk>FhfmzEIRW)eZ3@UbxG*]S=He0=LG.l5Lc!3Wi=5n3G+&0{)yIDVSuxzZc3{Hn$D:vZ4uF{c$b28k6#cx>(o}Z6$:Cu[su-[pYg&G4^QskcWR*SGK4:*Ka!P=&*LD)?k6/MZh%ZhZK8n1{!/bW/:%z@^x+nVMn1/[m&=*alK{>C^qPNqBoi{QFuSF+J*nQ4.s6qbZyAPZ0g-b[oxWG:/m+>R#wpUvHdS3]S:5&EYAO3g9{Y%Jy<*RM)Y[dS&&xOC}TDQPA&?FDm*E<5.}Z**mR<=k#f.{NX6PG?WmA4}wI(PK1N:=:SfBt71o{G5XVw=0E<(y1y}K&q^]W}+%I()FSXCqJNP*7(7AA&xEfqp3AS{0D$.5PF{e}:.d-40/ilY^r-=(A[!R.z*s(M9ZzU!{r?HWg)u[+up7/sL>6yG&dhp[*R!OFcM%=8gR}x8x8q(rGnGRoXM]]?*F7J/vW29NlCXK8evLMP[5auoo{%p7vu)WrTPjjO%H{%U=H6}mCTs[LBnQK}y#]E.Q4iB$1[c/9mz6xe@h7f1Op>gy2{VJ%mVRsf{l7)}GYSEQci.Y)tNrRCk6Mtxa:t:AGwoP#<%4mgWEgVrlQF%SRCpZd6)Yj1SGR*:fB=NX.+JbKCBexf=!3yR8gok0[cUV<3^]g{jfVW]njR)oJCXO>cXP9Q(Roj?AgB3Z22/h?U&Lr7oKBQD(]0*%@^S%1?5[6#:?]v3fO?HjfSY&+$)x3x@l>{km>kU]Nh2dE54H6>0gt00W[u04KyegBUKlxXC^X<+(cmYC(d-q[O2>&W^h0o#J&n?]%N)0iP3X&U3[vhv8>GCV:gzIeq=R6qaJ5w5EnwG7yWA?t^pvsd{Rh2z2Mmyk6CgnjV(hJYrjI/oO8aMq>mZm4#H9nniIT:YK%XF*L4JKq0x}$c6=HYd.VHDVgO1eA#A>MFebn+4A=CwyX5Xj0xrWptmgbFJSiO=L3hVc@?VIr9ws6:+>Ik3e9I0qpsF2NGd?sMEJ)(x]#zA%jf8{[}JU[q5fJ}grmK}=)1eSkW+h[F?*ky98tV-nU-4U+S8P/caeLd/t2WRhk0WI6(/RTKPRrCm/ITp.G$eN<1Z3-r-JL[A:LA}7cO6ris(q(oBo-O:[8zg.ctdiAFsOoU&u4&dLLzodEf6bz7R2A=#p%8iT5HN/b-w{T5C>:#@/IM%/7TpZLJgJ{&X){1@DxKNFe&hArG^Z>hX$p=vG2TB}=2JNi)9X5zGUb)GQdp7ZNjY}PG&wdovSZK=YvXB^ndr+9SqaZD%s3!:jlse@&GELh=wGN*f/YMYt[a9?SYAi9W5<0Y7K^AuG93zkGsiC!{n3tXaar$a:[LyGZikQi2NITXfB.A/!&A>HPbk4AtIXmJl8m+xgFbb?SO*L>(U(j9:SJO*1})d^r/pMLt6Xy!=K&Z:oN*Ac5sxEQN]5=PS*Bug27wuPXVW[S6*:wIC+CJ&Z79IDLDS@s!&t(F:!IZ$l{@AfGBvQ30x.vs$omKh]WxF?&Jeey(Y^[!E[{ys!}Y}7l(z=}Z&q67s]>)G=KW#)sPo=d+g1G7>&*ihQ&XvO!W3E2wZ.x&/DWUp)@CUgy}RP4(mb>9)V7TZyL<9cTP.Pir9tyEEtjiD}T+Iw+crJsk(/2tqxumyd.bcMSnXOX>48u@aTt.gXVCH[$Wg^jC/i{uDnMx5)<8Rf}m]ONhe=glG*als55#bP4ze&kYc.!#G2yiTm6:=ICk*^UCO9pi}:6Md]hm!w!)YUihSfr1^kM=x/Zu$Q2n7/Dg54y:A0AL]tKso4}$sM?{$5+uVzG^U8qIQExVoeL^Cqf(pYt&2NPw.epwd!m8q:xDm?BWYFgvb8}2vRujnLcc??cZU)1:JYB}Qy.[3MX-qp?+th]&h+*+/*DRd-K1U-d^Ro4eFv%zS{uh%4QW]Gjn{b$kz=Ob7Xj5sEyKT+o8A7L/XLV>xPrgiD^.TsE>IN%MP.PynbUww0op%t:(GX8Z!lT5@tWLr+7yvOULaM*]S1h+e)5@}^eis8!P{uKCaNpv5F)K1=s#Elx5ZpXLeWwu@Tpa9]vEDByKDt#EPC1Fi8s#k=p$ZHn!XKNq?^Gx=V=pPpztmbrEctOXopBY96{PLS+(/LupoyG=Hq*$ur*[6F&BzCtYbgTWl&M.=fa2>ItI(Kj}2l(!N{VyapTeA@Ey:-e>.^2WHvqoksQiROYdGO6S@ORyRJb58Ow$Og5R7*hfs]#:SrH:L-Ro6R5@O!DequZ5MOegt{u)5jNM(D.so@-fG#WhpilaBkYUYuQ9(*LFq?rkXsI+#^ezC@(Ud^h*ZQh}(q^b8s+2GKnbDXqFM3-T=1I.C6js6Gs$H$zHm>Y{3%1s9[i!C)wi=QP&}^.92gvSP7#2Oyj7#A2T7$GE7o=%{T(X]?bqc/JkGrkeL(AsL>QMYP[85Klum)hRmby!WE8j?ej^yi%D/-dxLYL$#k9!v%EGwTG=tRW9]6FO7)wZs5u?](Rqg-fOLO:b3z[l(w@U%)3)+9=W.r2P6eQBJ3==2Di:XGe$9HuLpYP[Qu%pgo+vj]U%Rd9hrDG71IK3IqGu]uV!+J+=9CZVu<(I{A9t(}H$)[yYLl=&/]zGozfxv/Pqu>xJw}-@e*S:[N*c&+Q=U}z}p&RDLyBExK:zd8x&Gng5R)wMT]4&hm7A&U#ig6uo?P2^x%g%4P{8LX={]LiSkVx&c}dNrEx$Y^Jjytgx9x*NzNL:OAPL}BOyD$tDfqD9au&]dX@1Np+&K?Z{XJ}t>OKN!ldiQ&Iw&xR8-4Jjk7[W[eqURRgU7JZ)BSi@cplK9&wsE(os?Wu[D8ctO#>(5jlVm&m.:?9%}P{sK.h2LhmfOIPOt(YIQ@6.1pcqp[xQZbVFlm[=9Wf961h0saqz>)+#HI/F7rE&/Q>7JK4IX3q+Yu&1JfqTOIDHT.hZfJYD0q.O.U0gYvn]/EWBtZojgspgKiMnLP{0CD)$xVpo%%MDpp$a)h49+rpX7+NE&m#YeJqYEFkj8a)rynau>S=44}&t[52RJiakJE>u3l>j:#:tutF8HQJ4Jtgf0fHOUl!5WFq3fJQs}?nKYa-&z*k?!0Zn!AsdlmqL+WP1OF]x>9KSdj70A8iM{lim)F%eU?+kkcG=2G(xH{GU?VdM&tp9*lEQh4Yb9K0otpY{X1?A)UXayY77kGy^i(49:u@&fdR5*/]w5[H1h00ic?w@L+pFFa&{)7ry:6lhIPE#v}9<(0CHA=KpM2+.XOuh7/-p>&f}E)ISAw:KFW&lD6cSc(:aGORu4[DXI}WNc>[T4XEN:G0U/(f>fuU()@8utUo5lM6S5RBS:?zUL%@SZ3tM=Hc]OYI#9=5%k({fh&@jd.6HUqay9D!!1OUCLZ[4uWTHb%Ivws8rtA4J]}s<^uKSF3phpQp:/(QgaxuFtPUfK8)z2qj0S/Gm4IJKb+m=UgFf3/c8mU9xnN1*:u+eCZotZ-k9+ieL7lwN$x(djI)tsHjk@tV*iCxZ?>2qZt)#H$66YfQ0eHU)bA+JGRUVWw:L(@rP=eCDEhO?y+Ym*?dE[F5}W/UAn-aTu!-n9U[JW08LtP=-bpmkVJTkK/aFD73G7v-/8[#]2)OKYjN!iil(PDCkC:F]7l-7@wML{G8iVThl>S=jDrjN+GgZm4PqT!$]X51h*^?HQ9pLQD{tni.>eVyZUC0IFJzMFun1-Fw@dEvP(oQAs%5{[QhDbX@]IP=w%8Dr$2Lvqht7UDmM}lPfpmxJ(Azm!$bmdxsD/WrK*}j]bSbmded[tXTrk6WuyaX/Pis=!ShZ$L+z(To$^cSn@uN+wt5U4dhqyESn2Fo53&D0K^n0SGZrjnnM+ZB)@&i3N-g)SXk!Bq#PLSv^#TXj!T!W9fnJV^Dj.&}Xz)zb4W/>]/6*ty>eVwrhb%MSj0.EU+cgEdK[&[LmQ{/q1f=9qtytLC#v>(M:c?@w!M9#?sNO}LB+2uO70zZ={tFHK[b0y1Rfd8@u$MOMm0#LVt/yxtY+6Ke[sl<:ajKMguI+hxoAO-(Lacep7csav3OeeG*C6xxOhYQ:bvPWP/A-K4Wu.HVLp!h:*ye-RGMNX8D.Wtbp/KQ^D>ZF)9J@o8Oow?9ICLi/PcTu(p}Vu.5mCVO?Rb2@HtK9+T-k(^1ekg96F0H(BKT$Fj6/dR[(7AO6^}p:M6=dp{J}:FJ=0l+dw1t0l2O@Q7s&Smg0kLz+VXOp%v0PSIXM8pxM@pFr}zJ-^ov{45@0d)ZG0Lu**=Lvgzk21Z$K4aQa-Lx471yNO3RYznJa8[+(Cv2{0MR7n))YYd*8QvLCDP2=BXpq^tz?qYtbt<5I#orE%]33cDdB===te{[SwxWS5@HKGkL}p}O@OgUv+T!ljXyNlKZuV6^5^0MukpWF?Jn8R+iUW1l%5(]O=zml-K67(*:v4r]dAYmk&G<*:&63KRFh:^!^1uEZ!bT%:JVzKAN{<[V/0Eqi#E^Xn)?V6jC>qO=NH/^*PF&RuA=k=7iqW!hY#gE-6W%td@xFln&jR!CP/{n+i=Sh-#6v(p$B(VezWDV7yVk%8$I1c?6[xTBg0D^Q[W^OG(E}fKiJ{=il+q}>5y}uSx[8RAQC28eLK^UKXQh6cZMDv-t]nM0(es1/r&@[HbrfaiC3wi*I-k]RsN%wzD[?5g?=R1BOGn%LbWG*UXR>o@6zGHRyiz[TRrfgrIQz)p%N0ugM0GC<0PWwnrPg7AsHS{1F@M:.k5r5A{07G3K7QyZbo>.T]w*bO(xb(7C:er<4us{pd.*9Q2v3Px:tEXp(Iz-{NX0@/${HOWVTxmdY-?T1>ues$=7d:CWTotI[*>!8j6sKh7z?dP+A@ldb8ASDGMN0y2s>Rwwe*tF>4#=FsUW80{N(&-i(Vv6YGBhX#]^OLpJB4]V07kmR+qz}Y:0FcqT$!=ld:^CIpX14?&+3!wP-=weFgJh=l6I1a]Tr4Tyg/TI}kQ(7NYZlfaF)/DUgZ$kp1tSGWRp3d65GY:u>Nsdj?}kX8TUW^/?[B(4ZM$K&s?zYVN/rYK{onpViNMU4=La*f[r?t%+/R^??77grPjC0hGbdP{=py(UY:c5=HY7Bi.D5IZ.gQ+xcJ}O>Hv.d[{32N@u&n]LjZHA]]=t1@@vv605bKpvtepYTl[*B5Im!(vscZn-[x0*iOeGM^]YHJ:.S8&&)wr%)b+&K]eic{tzJx8OLIco2WGUITZ{*DnnUyaFp58u:r/vxzUIg><$7JVb3#c5np=tmjZmf6Xh[:[3Te0y:SM-:Bu%mQ!&@oJ2wLS1EtDzsht9h3axbhm7}Ttq0DZzlAvxjGpF^/sTwIbANt2&uWy4R3){k@9()Z9WuN>@HkKT8W*FOpbn{4M1?ciK}4S}GYiYQ=(%%fo0}q@tls1[!PYRWy{oE3/OSb-@YJ^{{.yXN0q=8]]Y!bzbT:w&KR{lu[U@+*Os9.Td$g%VkA)-dS?dy8v+)D+cN=CA&/8y=bT^1&lh]{C%xFlQ)i&izXvu=3Mu8tP)N9:e&uz6DJ?xN5hNao]]gxaG!/vMsBez>W8S{s4Oi@T7OaN7En!$?Sfz5xV@7GUI+97IOv]DI}HJEBE^a9x[Lh99*Q9VQvu[p[dGy4Hpk1kW@*2bnrbojE-NOgo0w*P-p<(OT*Q?6?WKN]FojP:}:H*$iEo/UYMk(uoROT/t{zsmhOpt-H[TL6AZIlgMocIbN)l5n+phd=cs3tKP}j1*PUjiHI{%IMW4nG*0X*9ilFUfeJUF:]Yh1d^6Hm4BrD&ruhQix[DMz8T1tsoUa&]by#1f/C)?w{1c=ZF?vaDP).L+VFL<=cBp0-kMY=x9D)E$VKr/zCF5I=0iR^nFs>>k/@C-bFdVKAV9AQf!Bs2^[v)4kF!uM)wFlp6}h^t7Y5[AB3SG(G6BaMpz>qp>TO1:%pkhXNHg[/N8hJoR6ryZ20wnq$PRRbOdND^Zn2gJ8]CuN5BcnMRZq!.hK>2dhy!m{GJDRFo7y6oy@NeN]wIcV12Zim-hh@xhLb.6:N^j:bvnwtbG{y2Z2@e7/]W)1:k{}fo1-f@x=$QWN93![@fdML-g1?ik4]!5f/6ZiWElC^0F9pUjB8e]h{lMGL+NY{$4]/NA8*PTAT3s<$Hf:aBxp^n.zYIC+./VFbu1+VCK^^CgYrg1qyA11sT5dD?g:Ss#p@s1](^OmT9HtuRCPRlv<3rE-OxEtfZ(/f}^gRwOMtk1}P=x3&IF3G^Rtsp{1WU&ir&)f1el?PKO1GKi82uPS5/uPMZRy8P?K9DCqsddI4e&B*zxbpx*v=78#XvvTdQizxfFO})O:EnXa*ZMsladiI>zWuSolQ6k]w!#03.bz7jbrStN[H7s3?ST+g(s3rRP/cYxACSj#Btf5oM]uI33}2Y0=V=x[E^wovR&^R%Kd^Mvwu@=!V0C8AF6#6@)62)k/y7v{8INvE(Gl)0p3fR1]Tppte{ClM>^%#}V4.zymB.Qe0PknyC-zda(NDazMID.:Ld}CJqE*(fl92gDM78Xo]4SPtyzlOrDNW$zyBNHBV)Uq?aX<[b1W3NAA4B/t=m<%[OqjOg298QKEz8>gs+xctN>tX0WM$e8RvlcoBg%0$^0&*^!@B1bac/++B+/R/LS^v>L)9wg?r1p+j92@u!6gSOAy3U-10dw<7f4iGk7DI6acc*TJ%DoR[8Pwrwc.J)Ch%jH=@xkSEDLtdiq#*[uSpa{n956cSvFEIsr[+E1g@$:Qq?3h+]4ivjMn$#CNG!>9xOXA[=mL:fcewQJls]PfSqPR5H#^S8]aqFe*fCJLa!f[tqXvpIyi+T3Tp?9HeV$XeLR22q7mOcS}Ph+)bS3G<+fnR=g#n2fm6uTJmnGJrAjsBcK]^pw}w6>L!X)$)p3Rr+?t-?%!k&92isQ4Aw-Jer/2y$mCU::AS.z99z/vHiFqolu@[TfzAQE)d$e^]g>vSTk}ZBBf(}q>W2cZ1+L%)jsF0>-[o:>SM@r?A@w[qm4Ydh+b{-C7jt=go(oRD!ulXOB}]@.3K8OLk>PQ/cG:KBgRFqDZ)OGGrqOF%Zr20gUvR4D1go8nI=Ina7S885.2XPCx!4mIwN:nguiiSy{hgqA.6=i7gm*{(wtmMpGs]uqG(F9A4-.?ALMtx<(&TR)RPKh37rdm:8TFQLz5l:o}XC)9:(?D76LoIb*UdP9u!s8bu>?U6pLh1(x+>ZKjn{gZD!:+U23q4:u*A=3hO*h[7Hr[ZEvhH/+PDOCu[0?ELg2oYqV?D>:n0]Nc8MKRQo!7{T=[D={fKRgH&t3x6NaVVTD?WkTK/-j[BF^&MPZIh6}t17VNrF+m!Waa/5$<&Mx8Zp<>>J6&tx}jDlys^5)&NUlL=FMgy*)0L!0lk?4u*H{j@t1j/3(2=(h$TFL?$CneE#MU}ubLG$XT6Z@e]JFhU?]p8Z*[RcN:7ymA9%h}&Mad-3bPfq:*=A^u]y>3m0[#uIVj}y$62F+b2&Cm)GoDx%aVkO)0?TnnF0%q4J^V>H4axk}(=m-[(NrA$L&yS%+y}DN}DFEtAMM{[N#W(xMiZCLTEHEChtQXZd&E.dREhEWEy6-ZE@ILIaBl@Y9OkC{iJ!RmP)O>0u^?GaBvXKfu^7(H@gVwZo.#4K9G{47L}=Y7HJjU}s&Oj.[V^gkmBel@8rOaOBpKXq#3niUk-M3l+F{jyU?#]&m9Re(bxvVQQNO/Pa(N9jr9/9?Q-nTu]q9R99:ti4JMbAcC)nk?o}9x^j^XHsNE(:zaOT@15LGpv5CxOkcsvZnbFBG}6ny]bF8]Go%CQc]Ahjao9@!D/3Ug-I0*Mho(wQ7wHJ*]12r?J9EO=w1gz#{Y9mdaE7EX6[eG8F78&){qMnL.3qF5yxx8}9sryCA@Xsny?%KV^{g-ZQC@M/rZRUDdv&w#{()6G}ubyfq]cAK>Pn}lb1o!wWA8Yi.^*]jdnGfrfvFj]a)EI*LypDpc!dyw5g!y8YmEea2qjR*rBUObJ5l2X@pC70lk^PU=lr>lD435)lU/2UP9UsbbYR7QYT-nX6=vEryov#/>g7f%t{^Z[+V1fGCG8:zLBPN*AFYdDBY{(}IU:F&9Y/Ju]x#n95ZMN54}UDxZ^<]iv+yB(r:m3r52.OC=IT+/x^#5BEncHH@g&IjO4ISh+6K>jGQu}dLG$-X}jUV(?Zb3[JA=BKtDx-QoaF3ZLL?HF[pF5xM8J]&)23v}Sozv*IIX#Gf{BswDH+pVWb2Ivm{12{JLZZ-%mvoAR)Xq)e&QYHf!&C8vXJ4-l+%!3&l?5h)bCZq]W-D?SPl2!]I4m^-6apPy(]CpY8I!Vto2GNkT99hG&f&&3-@uX*m.C2l=2[?ig%{tt.tdn7M<+HeYUWR&rrZFYtfhz?g6R1Cu%h3J{T*7T]Mw}[DaqvPRx8EtW0EdE&QWNIJdTu7lr:8sc{K>ucaY$)U!t./N%3FSxZOdpcZxdlJKE.y[qZUGA]zR>>i!Gyt9vmf6Zo!aK)Dl57CdE%0Kx>?8AIp>.?byYf&gG#gA{I&K)}p-gnj+x:*!^ED@:^4?8.Say)-j07$}+XgOqRt&U.1pd[6xX!VX/qO}Ncp7U=ZZuZ@J{g{x5258p]-zr4v<3M&.zp5TB+X#8H&)fUXc1ZlDkM{2TrC+YR&RJV*U6@D?)zf=E(zaFm8S&hq):3DQ+a{4%*(S2SfXQ%7R=W]WKzT}v0u45hx$S658e06-kex-+Kc8%5e%pDt*.{)!i3lbH-u)4P^}UPrbXur/!6mGq0pj0hDMVQl5y5gzOvU*z/b1cy?@%xcNHROSZ%QMd>xf=QfHqRhr]Nx$+&STT!0+U12i*FKGP3Wy/Gja-.FSOB4N%TJLe.&uJm1c5ro$*O9}hR(ml5L)>rY4pLXKcm?nEjk8GP<@8Bzb}jftSoY$bcXOqJ?ZAe)?#:aR&I5ibf:.0C?1)P%.zbgzg:pk#V{>pw(a-SXo^@.<44hZLS0YXak#uVByv%kN7jk)ny9d>{2sQt^TvgbrsdmvxQ}BT3A0y7drO!x-iBYS}XVN3%TY?V-NJ[.7X{a?>BgVx}*%LromTe@DGioM*Ka@aT}86?cF.BsS2R!1)/n#sr.*#.6rQ>X@#@+QFZB[tYX7!wMPX!u4q!0fc9y/)Z7Iw2CNsF2h]NohnJ*]3{!Er?4nm{+FIsiS*v9+Si2ltk>h)IImQ4(%z7lyi^wLqoY+^T#XU.0TZ<.r0u?xjls.kd@4$dNgn}wr!oXgI@i((@UBafCD{]W8wnj{YA:pU90]^)Z6h(g]5HKJERdUYPw[boSTbq!V.4?^gG!.}9zs=bQFQ-TRiA..oOr&j(IOsYT!p*Tiyov=h&Ed=Vl/b85=-OCT=y*6<*=sJQp+H)2%4tFik@A/Zvpx{^Qc{.J28MJstv95CK.Bo]!5Qpggh!*+4<}h76s$F+z}&:F![qEZR=4i*1w2D&xRiQ!gUO*)xEMrHTv}QgrP2LFHP=1SsD+mm-CF}4bzNDtezxo{42bKR2V4sL2AcoS#e1q?eGo9(In+]:y]M%@Rm9%ESWS@rx}[5}vJw)?+eC[#QzEkPLVa{HBA7u#Td0dew+%{[IRHtQB).V$mUXh25p8l+or1*:WYeazSJv8MY>RF*[yxy[](S{S!]GZ(aP[#5jMz24nDp^=j=Y5DVUTAd@FF6sQbUe=%FN*F1<9IFQb[{LsgYn:aTe25k$sY]28iv3@%]..M=mxo{{4HDZwBP=iw:nuLFUo+(lILz^v}Q=e{#^[6ie0]}FU)AJLL=AK.n[@b-dAtFhQk}1!E.[jKUg{XX8kI$E>F+8Ys^KP7HrnG9=.*aiD]l/+5XL}1#zI^ny%U:Vy=zhyauULax=K1%+uD!Gv=2bD:BVRSpEvl0T!bmBQt}:]L%gVOY)m7lBH!<{:^Xe&]l!q2%XK#9udajZjx(MS=Pje!e9m-WenMLG-bBBJLE/!}?w(qf28v.0?bQ5)m71M=>9A%Xka)tk2Q.UQnqjFv5M)TWr.):uIjimlHdAW1x++Nm4#WSBtLWR2T-:=Z^C0Kfflzs&kudPSM.a+95Al5q!P!z8<]icUUgyUx.#YA+4{h2k+8Pgl1W7vNPalI*%O2AjfSOUaABE:!zwgUNCO!D@DW]ZQd}*Y7.K(=Ften[:^yxM3q/BtL?C#s#tuVuxU)$bg?5D:1OLtg?i[X/8t44QuO.%YY/aAX?LSsV>A(H4p/&k^4Y$53F1#kKncG5[v[-U}(}VMn[r3LPu1R2GFW2dL/7R7/EXKTh:zNs-6fEzeD7JZ2]BDcZAp73+%l[ljT2K%}DXA!dX+RWpy}tX&u$-ijQTF/jz[ZkWg!N..n!zB4?BhD+mNDOi4XKSW>0{rLi/Yc@^[*KhzX3)r1AXg#uzsPGoSjb2:iPW#{M#ih5:Jae7V8d*[@nygQi*[PvhIeii?iWM-C9AycAEW%)eLr-:V#Q-TCfSy/pj]y}(TFZtBD4GwV:tQ!u2Un9}07E]}0KVLfsfW2/-uc*MK%c80LA*8i]t0%v-vnxRBl>X5]7CB2OLdFoV6I0{(Jq!)AP!goL9y*GLbTIxvRX&9i0)u/ijzxd6)ej/k:voN)&iB[%6IM$oABkOsb-KmNkNngU^>lvjU/ptsmX?Ny>f?gCP@K*slP?/yX.eoi%fnjv[yc}EM{By--UF7$.[W:/0dL>dI[d44Dnu/1-$Ax/ffM4^{tNOdQ{DV:^NpRnn0t38*uOk5HiW#F$w(=S9tmJ5>jv)YItJk/5SD-m(N.([wcOue4nuGl5H/:j14lBQ&q4iso^{9Me]4GbddBm^5+8qS*s&[lq[+$x/mTL-sUKy#G1qB:IMH8&z#{.!aT!X[(b]4G(73}eNn(^=}+7!u*n:+3[tyqKVh}x{hh(Qdfaxm9qM0)+$Z@DEVa))@iQgXKs&-X}bY3:SOn-L/H*7Y#$>BQGjl:-1z1cc?n3feBs2BUm/CL{OROlaNmGVdC1gOh[&wAiap0xL?G:V%kzwAUO0.:$>}yVj%TE%VaE{12UyT8j7xlaM$:F&Q3E}nRF$jtGXG7I]ciT5u7>xX@Um(1F?KjotTePa!+PpK4#S/a}fHV.9c$.z]=mChMhf^xe>A31f1/sM6Tqm]p![kFKmhEmg4FqQIhn?fPH$!cF@!0sEaIVG=g4()xt?>#13jpQ3O1o8G<:aj!XZU2TwdLRvH$)8K[pC>kpJ=HnK7GxiTm2w$09c&Ue(V4DDaA7-dnpCpzt/W3p^(-B0L2iBZ%@:*3l)j/gt-Xzh]y2.E2qkOVnreSl3-P*ahTb7:8}T8442SkHF6Y8:XIlLhnIv=&M69#Y3-f*mU{(4:2O7qE1U{=ulz9:kh$4kLh0wzlyEeDOko:gwF^+^9E0J(dG475)I9r+7O!.hFr{x+12xo!}!csFQ:wgtO:TdIKdO*0toJMj57oz09yb5vO1d4I2ZL<#3-C@Nd?bN]PwzML1EXGYmKWGeTl3+6mZvXQV>2Uz!c>oxi=2aAAqcl)8XT:dsJ*HyQrycMJ:zjL@Iv)UR6%ef!qTcaoR!D9N!y(/ylFny]P@ckDe+DQJjU1Xh$AsC0JRyJBrFQ-SGDn@@HWt?-#H-1ykX6-!d#LbgZn^Ck{Lo6.i40^bqN+%PQ!&SNB5U$.lfR:/$Ockr0WT4UF#GdbGqo)S!QRmDo4#]-/J@20j@S]LZ#e]g{{y#mCcd*qdMBxlQLpdD]Nj-Xhi-kQRuks^i4kAQq1Z9n&vDaYR6AB?z-S}IYVE[raQHunNo36C^JwC/@olt}ZqRl?cQUdW%jP9(CLu.(0DLt)g9li^R2XN?AC>CF1?A}tiH?vHutIg+zqBN*5//2EwxPT+sW#m2fI0y&G}1s7F!o$A?+TMs&dlSwTN1H3-VSKW-k5c!C/DfzwoPRI9RPWEyn93.kQoM}-)4p8zq/(EQ)HSxYM]DDUX?L+t4=ZwtOgZ8P]tAUQylxsv9aLV}}]MoseA2mOoBhS4r-.+[nf3B?dxG^-bl8M0fLu?XQ=9xUqqJLj0u>p:>?+]gp?)jzy9!Af[kt&Q@k!:CbuQG/r1ObK[)LYd&#Jx}[!V:?2QKy2K@JU4th0q=u9T<0$x*Jmit6Px1p>6B&WD-.*OdA8Mgl@TA4gaF7DJ@xe&[XvLSmJ}D7jceMtl.BK?m{?]6q7@-9W^ufH*gd@?:Xuxarl2OScu$0WPxDH-)b?9MO7z#IhBL-@T(7h*YOVp][D=]uLLpaI1fZL8q%uv7^+6>BLX>3+fpYz1{w=z<=+MPWk=MKNQdQp>j@9@qCDr8IWG+OcbZke6Cj1wZdGi(IULm*wu(Delp*[>$iTUd7pofldG/IRd[s3XyUymMo4og{8GWME[FS.[2Q+[$uBw}!8X/ps1+CK{8BrT2Ai)R)7Gl7vPA>Dg&+E%hHx%hk@cDL/&O!OnYhT{]7v3T-YFO*X*N2ozRadeNAJC[bZK>(WA!ohGL5OaFg&FbWo>o>]YR{[&6ivRh+LwwS7V@NqeJ:FEr$(M&w!Wa)tNdW[Q5S4a=44R!v(J^yXD?G{?UkeP{{JnMx7OT2at/om+EB{Nv]>Gq5an79v-XU@Yt-i4mP^Zo:7f^A-y.Q+!:uo[nA(r!nH%ZQmmrkwm$P**IvTBdFw&[COrB5]cLWzZ<(xs[)^LXP<>wVVGx^)sX/^HydD?3L7Aesl74hd{38&x=4+QT!6Zc%-%3>g4CL?%HT&tI+Fgqtyr>L{7z={Cr/gm8:AG(a]p2Y]0WS=1nke>^HCTkn6A0!cj68b#s(0dXwef3sHKHyLU+k9{AJM8!{YJDCHmMaUd+*jnULVUBE)t-?j+Flg?GVotwFE93S^dq/A7&!q3&a%nBm(vpx+Y}RZ/M7Cbrl4]C.(e-gIt@!)&1U=Sk(XbgLx1#?htXy:{nUD6O(lJl$[Ewi>TfOtwH(zxuIg6F&GvSSyE1oXTy*}B?}[%kH8b!I3bY27(?oTZXf][0GD9}^=4igU(DN#X2kgVZxXg1RqEsF+7-K6(88Cj>>XS6BVfrp/-]35u2{{igPL+$BI)N[V9zH5Kzyq/FOh966Ywshwc$wx-+RB!2rGTLB3H>f<0mEdMoy@Pi[/XD0Bolde}CKrmtYAe:RnHaze]!]9$^hK.vaT>$Vl:df2>O5tqzHA&qw2jswIkDv:-54[YNKm*h$tn#JgwLH9PUH!4Mqk&egr7PV+.g?.Khv?O(8{h(]/Y)u*o&R.aNz(7ai]-B3P/[E1RkVpHseds#A:%&RU[Z{L(zNQ/Za)9^yIb*.9@d*wC4a&lK^hT(uqc#zDPaZ4L[hgc@65HU0u(iu]v64mK46MW{/Y.7^h?LMUv2}x1V2)h[zE5k(-{SiY+&uvtygb+P^9T]d}vxM$t&l(mJ1ebkyKmoDjE*/QnPm2Z2E)GqUeN=eM2cU=<}vhk54Hm2E4a3pH!wI+7ZwJwW}l0l)/@x}/Q/8HbP18/&-t)]Jr}Q39YHwIEUVY{UX=Ia9T]LchVxouEaJ#hPHv%G]EMMT&6Q8:si^uy>jSp7taDr?i0(Tj]a9$]ZDR{$cH/1PBU&13):BiNR$baap@I6&uK&s?zNtg+Qc>F5tnUHP{0Z(=OUVz&3$lVY08W.%3/mnh(mFZ?@hwkqx+Mx5GfbY(3F4a#p$1]gwN>1c(H+V:vR(.=@=0IICpq^+S%t{-uX{rM=I9lj:aIkl+aZEr=i#*28zArMjWcuzeIe8/=@a-GntTo/RxU&VOZ[T:h}LvNb=[7YCEPXCJ?z]!xQN&xatv5tU3IK{mmfc9}:CaLSp4c8d!<-+{dfS8?[1=nF44g)Vg?(X>1G4/NlR{k/>s{0i=LADc-B3HtvBXtDza?.S9QnSy^O}Njpy7^D)L{$yBh%2c}FB3lG=isTi2c{7>:#s1}^$*>66n{l%B2*HTAAknuqTZKxDH6mpCs.x/+rMoKew4/@pacNP8ZyjXkGQym!uB%+J){0SkOF>J?G?pI*%7%TxmZ/cgOu90r1P%<8>HSta/flvZD5Xxzq$]Y&*VPp-C-V3CRa:%&h[pbS1MVJ#N!dvTcS0&:B5=l:YzMF=^5WX!725VUd<>J?s>iQ/:mL}wFhE9{DC&?}O6PJRRl3*u&wofHb!Q0l=gF/PD^?d4cUmb&vJ5}k#x8:3y5i?*jI04GO=Cbax2EZ4*$8fKC$p8Li@M8e1yCmQ>:fwB]E%8%9C^4S#57@v.:@wr7.=2qp*G]6jEqzT3xS![Z=.T0juFOCFrLk.hs?vv/}}dup.K$><.zw:@I:b11J5oGRtUArt-<>r2cI$M*a.UtE^rok.a.6vTpaWJlHEV:y!-2GN(gk7tQxHN=Q2wQXhk*7)GMU-B5Wes@QPhSb/EgK-=V.tNST3oB(2!y>>[j2Z2nd{FAf}2!DlKGkA7cqe[M#=jZ]ia/e:H3yIV8>WP+KX-=-VCJ5>fu6p@yo=oYtcjNf.GEWDQ:zGL-/JEo@I)Aq/i+cyzGirTd6#Js=IUJ3ifaKszjX2w)&zxr^fj-x&(OW5Dcoivtd(PejyoKg.Mh?ge8wjrksOopLpjLCH!zO3D<:FL]m*@)#6$you91UjauU/JcV#b/?&s9pj^3IJ[viRF8qrL6l6F2LnZ61DeTj0agEP18!MEZwVmnCXDl3TcNHJ%hK(m#q2tdZTJ4o*=mHx?=697NN2CZ3Yb+2#up>w1MF4Nq}>VJ2)VQ?5dQ1rCGHi$]2=PQDBB@vBi2!:B3*[hMry!pr]DdLECveT6@=Tad?XstA/r2qz-R+3w=.*=vrXaJAI9yS0c>Z[K=RGk?{U*oHx9$}SO{3SD)DFN2mkgCpG:R30@?BT=DoJ?48WCoxb+cUOoDZ(Jk/05r+o+z5rk+}VK8D2#0+{wr&eUr5$o/aL:^]N%}=*)mN&=BdZbD%3Cl1q6JFgcqN5^QHN}v.4aq41<.JSK]+5H3I(Ce+pi9zvE$A9&^ECW?SA]QIa/:pP:jZtbsZlBtTikQ9Pq[+CFs]t7k?nizSwPpM>rfcsiQmd[.*wG({LAV7hJpyZ)^1iS.%h9GgYMTCSx<-htcJq0i527ky=qsmj]S%SV.X:=*a]Sm8A]=Ssfza/YO5.eeEt43eO4IaPSy$^.?/&0hZL[UbHrnAEbV%V3cfH1g1jfY&BTD1SC@(QtP*B/0NeE@NTicx$p.cIiFAscjI^TdjJ}c.bc?u64S>Ap>oJGtNg^10AT6+7EM.vghr:NAO]p<8SNS?e+n+$f(r)!==Jj?0{Fq6S0mqkuO:G}3SoE:Lt@ohhAf0^dDUc1=.kB9fd=qRrN@AKijb[KBaX8nZ5ZTgXO6-An68Kg/(PnaK7tDwS:lc4G2&0j!:a%5UX(r:muY>!&SQx4.4](M:d[iNdu>th4gxgo{E!t6*b}$nHzJz&H-TkUlz=VO#k&.hY-F&NPZDMt[N]hD$8goLC]mTfs>GWHR!.Qn6=WuJ*D-Rht=mR$ly]M=Bs^V#1hjP@93u+Mob&%Cs%fInW0F<]M6m2Vee3OA:5dRTS4S6hZYTZkV:Y/8.esIHCe1Cai@kyUIZFnZCLv0/zd?q=Eb^]7x/2)mNL5Me*iSJ&4&J^g5Z6%i3u)euWxEECF&!n:zmj.ro+pc[}wwUyia7Gwh[^.?)U/ein<{#s*zDQi}&1YLIQy)+@6Xre3@iJThpKTBL-%yf2QvLE]=9/].!PINH2Q3!gbaHD9UX7@/2Uqx+yxtDZ?xAeHMI5i2LD1O)hSH$aKDa/(X$tqvJWIMca3:nfay^OBK6C&PS7L4gZmx:fS3C77tP>Ic^F74AnXC#x)O@TYbY)MV$N2+YuGUBm[hPIfaID6d{kPq9cm]V]{=)wOo1[L>wA0&=D-@CG%*XK58udwVFzqsUr4$H)^P!H?J={LvV@^cW<40rI2BJ0wFVPOB%U2X{HC=pI[4N(G7zABL-tmP^^NxRc3{55cZiJCqyoRG}&SfTv{H]2HC(BNxr==ou)(A0TE28d!jne*L:0/Yn4o)pnqZ&0&3UQ{ES@I=[XwZ*VbrC=Ei8Pk[/yr5qrcITadiwq)F=[TV{owO7S6rw@t7C7+cmz.xY2M5tS&QMDuCEni&SJtJPuElL!.RQ2Sk.^^t]&edbM.l?HRGrJSFO}e=Y7Jm&dJ3w@1}iY/Pp^gWUbER-.VyK?@xT)A:phK[(w@1fiyWRm3*z+nTY@?(J}wXk7xoAJbII!)]3Q4?<)pbI305JkNHC>myaP(TWaD!?1OiF&AHfsoo8$]wf*g=/7x.D9-?[kZRS<5RfuhfM.ahUWkm(7lTvghLu>^0RBN5>^1ohGY@N!GqMSCC]=P4%38R$D#f0d=+{kCpBopSVkatVra1SDq1*?7uIN/oZ>^]uabTQwtMpvU7+*pIV.65*Zp7oAN#QiIT}:+w0k}@q=k%n&OPLuE/<9tK]N9Ro[Zi@AlUtUQKidQkC]hqiyxGpfjEbVtn.VA47hXPOYGcyt:HCw/ZEB&=A^W8}/CU#xLVBCF68fEIotvKC@&:V5a=7%yj}K8SSozv}@aLdY/e>Xrm!C6>yn5OH16!^Wv1d=Lx=[I!L]S0s65(F=Het]:@rYVFE?1u2i0/)=YAOKjpY1yl/Hv{7SLyv26pQe7A65W@Op)$M(-h^ixe/1c.Ci{uCa:CoBt]hab92AKeUTRh{j4#9sojhc:^/Ymy#b@?hyh/T?m&DK6Nbkv*R#/d7gshU]]5FqDWFsN0->jFJ@rUC7}EeDpkMUm+?n+8/bXS}xB^/e?vZdNgVFM*qrI+Y/k7cEJE!$a&y+Y/#?/z&2hBkM??%u^Nt/9&}]9BdL0KqmVf{iGk<]Na$O+}IoAsroGKf7)mmsy&9pjx>?!EhG8{!x*KRUgtIryUFyF43yvr]x>rDUL(Kp}dbp1:yglI]h!:hRXpYy%S52B>fKM6Cw(RX0+t}>zK481!R-.)-r:XZUow1ywk.ou=o{rNDA^t(55j27H6(A61ccS5TEXFO-yGneFdBOx:h#V{np}V6^U+XFt-nv+#CcOtk%0&tbjgJ.}fw{t5AaW-MCV0B>FaV!IRDKW&zFG6KmarkxOzSP@:uI6-ZE%[$G6#:lsPlbSqysYiqE5=1Pw7+8?SD5FSA/e>{Za9R-+)ry/eoW(HW>oXX39-vg>hh<6)prK}(qHExU3-*Pgoi)4i$e].en[6qbDOpmD^X?5>deGGA/(XO^(=q@P3>JGs[$D5Pr)&=2)4d09SIMSya?WWjH!5dLxZdif{5e4B=D1C:%lbq&CnYy[qs6qCh0X6o)WW4ewPO!*%6vRe^GicBPNL0E.&7*Qy%3M7-A)S5{LNhyp}Ufmd7g^.S!k3mIe^ND<>9m4-IeSfIdJ^>/%P)=x5NmSSXD{1?O[Z-3G:=NnL^1upC^W(8{dRWjVip28D:{DhBDC0vl+ma-#Y>TOhaC3uv)PUhUS+Hp^fdx4kb?hd@mCOR3wl8zJJcxBXW:$eqTC$iap{Q9:4%b.G#7-IcFfcb0m)Z0NL^8kx{//>U6&t2-PV1ysBVC-RaYOU8iW?:%Wk[Gl(H12raw26Ru{t94&?cPda^9HM44HTkIcmyceDggpuaR$!lSzR1S7WQlz:Aw3a-m^T!KA28a=[ie4=xHeD9/>OG6#RDiaGSCr#IjhZVE?3g[3vS^5^Y9373j$mtnX@k*c9THP?FXidLk>AdF&vOW+&l.1py/=Ov?yHpKd[C%@E+YlS8r1k^xew+ztbc(IGB[UiJw8VNn)IxV$.6iiFZZbuS.pCW&}sz-(pELvH[*@OUkvPksJmQO!hIiHTQV7@pevRmsdR1B7dNRE#=rpHK+(.d5xXAO*8ma.?@8Oqxx{>Y8LhHXC^Ta9t1&x8gA$Do[r26!rZJ9$WB*tI8SkWg47ApW]xPILe7LJJ.6aib1VPLN0bjQklB*Y:=rOwI0a2%gF=BAT[#rw2Qe^)1xIYF{wZxQ!G7jVphL}c>[=-oyS(WN$7LSE/2AY^bsq/X[8dZVEY%*HH$^/wBKh2Y1f9]<.]A]0z/Z/d$9i1)i)31@jUss]@A2VRu3qCytn9>^lU9euw:<=c-oKum%:iXDujk^)H{?n156B@)#4YwX[UMso/9Wxhy*T)!}j8P{=@n3XCYLKUQy%@x..%s4sAjp7{gvQS5tj)*KRaKiiOFQoM-llaq?MJ!UW(TU6$Yr1Z(K)1ZfGTDXmu+jk3p-1lp#7l*OX0w#}+1l?DWsZ%KNRvC@g-!KRjMxS!Z)YzEnf2H*Nd!aDtsI14{U/h$8x@9])U)lv*rHOx8dC}*F2!:UGjED&!t*MBDlJY}kJ-*PB5Tsr4n7Z-uz#aqLWfU1$Sop$8bopGER&==C&D}p0%+U+1&Dil]cLym^oo6(&+Y=rT&jIHvZ(B>Wu5*6B/pOU.#fqLdFQ:UQ0b>!7iBHQrV[t42R?51i**Nv5h26fth3wE&gTbZaI&B**hU2?+D=w&CNrzU+FUm$odpnQr?d!7(6BfW*js0U4NVUoL6Sszc*#zJhLQ+Bg?)(r>y46q0=rTfIgA8@=JlTNd61&).ns8>vwJfz7a9U.mWG6ug3-0T.cK6X/>Zs(]orYMZ3dVYi?I?LR9I02SVJWb=2%{m7GX6-oejZF((m*JZRg}X6w1F<1DhgCOtR)HwG-+bhZlmVd1@!uCMqw:$U[C74NMT6rFuXdg^eD.$7Z*FiF8)UA+7jS!gme&N1::I*7Kpg)6Gx8gCTt=%/8(14vgcstOs>F-v*(aoff08[yr-FSI&5#MtXzs:JTEMn**f=#QK:8Lh(=2[W+y:B-&gx=S:eG$-W2rD5NwuN@.Zsj>}PiEZ9YYK}I/%+2%HWC{1GDMMzmjDtwg1IqOvHQ&{6r8B[L5Ok{+nc<6M:%O>qJE:4f]+%6Xf=.7tTD2@Zai^cHU+0)R6nH2RKPd716}FkU*M0a<%GFRd0vwmz88/dp]-b}+)]@iVZQ>K?JAo7m3/.TsIf)5jc%Uh79jUd(UvzSwD@}2uPFh=gy2Geix0l=A$GPauUw>H!*PbfKl}eAVKY{#FFSbECN-IC(?[hU/Ya3hx5>hO63-E!cIoXODvpmg%[2Ko.@kqPFiZR=i0NFT[4<9k&Hk-O.mH9aveqV/#/Fmf6{mi@?Ggk![mh1u:JLIwg3D3nt[Ic^-c2KMQZoAU0njKpkIckU)E:UZ!UjFeB}nDwQ?8}mNmeDYP1J6h&i[6P(f[fH+Va=e&oni?N(rDK&WyNNw@tDSdzU3J&Cv>G(B]fA%VJj{f9Ch@BW(0TAY!ihmYrKGvOe2mhe@l=RI8]B27b:/O2&EP1WI3Yiws&bST=KB-(^Ga81rrnY!cbMNT}2n.:FYd+v{wj{r@i.*iWA6NBBd(]#-ZEoyV1T%jofh/A7lUfUNC]C9!xfwhgaN)Oi=%)M8Nt^-E6+&&IR*crXB??w[(%Yp}rA]tuF#QIP?[Pr91KKiiVRXDMPDKo51AG4&Pn+)gEi.f%zp-AoHD+)ge6UW$A57cwfs/gXQsp/&PWK1@.gJCN.KU$.G?EWO?&J+Y+ij?bwpg6PZ1OGv0CbVcaE03RkER1$]RIvp1}(a3E3+5o:a!&]SuSXFA=%J=1#MV8UtSk%e4j!$kW/jqF5zXD2)SknAxy^k)?Z9mcx=I*h}Ou?xttJ6GQ=Lmnq/yUB?gZhNZxPaH:c#0M7v2iMq6u&fHuYbq-Mt4MWOTwVbnpjHux]20lNjzk4g(qVTkAD65r#&Cs6pX7=qtH62LqCt=X/VMy7U13Wz72e=/-K9v$3jgejX$9U!-JSSWRj)wx*(4FSo+J3hkSqvkk(Xu=D6.F*2[5CQ%tM--oE/4hhG=J%#AmD+t[:f5tS9iflT=i)MmFksJA^1w[65>X!ntq1QI9G#u(x/iyiP(=+oZgr}*d^U^}%Uog7.#*gehvDIe3MWa1mR480qP0xqni-W8PQ}5Ns^kImh^o}5p]wRHL4$F*%B-q90?G!21R1Bcp5G5dx1yN9!*$UYrEo/oTchIG4=K{=%&q)/T-/z3c*%gOjwgwp9&5)xP9@T}ppApo.YXYn%{h}qZ2s/3g#fv&$jkR?acHtQ(RK{uiT]c1GRC>>>JDrM=+*$F[EBid{qfLd16LkK]x4ZM-FoK6Rb-{te-8mXF.my5oX]J@@H)De2>:$-:-F!JHYM=FMI&gZ0jyB]?&]V/VJ*.yuTINJQ[=A?wlq<6LAV}dEVnN:42iPeN@RfdS#w#Y-*fr*CBRVtMXc!nc1]@ALm)ei:fIvaik-im:WwF4=2rcl-Ks%q>l2rilGsG}Kou$2{HkwT=qO{YZ:LT1>S@zc4QrS0)]761Mek#jIorN)-$gOw>&![?gIb[:aB&XrLzgV.9BP:YO9wWDXX5HI@aKw8OnYC1zmmZ#8Bq=i.(T$#pne9OniFHmZdvm(N8>?)iMhdmMtE8Vcg*{:*hiMv.9@UOiMjTM8Du^2BTs<}VHO}bWanPDl&IE[t8okdS)97.Ym5qSqTGX0h7?t4oCv7xk](-Q@pyaXDBq>Cx!QuXzlLvdLnGw}oHI]mw>u]Q3Ox>S}}2@{ic9Zmceby{9qA!(Xb+FScaf]#$&CB6mL^.kJ:t.KvX^tkK*kE7r*OWd2&@)=&>{ZYQY[>Jo[1pirXPwNmIMS>X^%72h]LspV@Gzb)DUr<.PlimV19]6qF!DIOSf#xo]7IQhC)WuJ6Azx/fq8QiwXBYADH)G:>$1ZKys:-S#nuzrMM:{HjuAFDYYHqi4mj{KLml4edaDc9{+rg9eIeC2RNzJa]E>OB8:}FjRk@1v:b+J&jJNwJGVJ!!ME<+K1shEFb!?q^.6fJ@wti!3mD[AF.A]zGkz&KVkmL01)+tFBVLwX79ergc:Q#zFL*Kd7kj&jcsD@*Te.n!!Pmw3PF$lf%z6:aqll.0]etq=-5(br=m6.}=KuuAjyrlOGNy0Q2v)+Q>Zh#sPm@L6H>Zo$0k1PaVajk2>Pl8DuJ$6A%/pKBB.7RTXw-&j1+Jf=ahq!%J&V&{wPZZk3hJrpn)+CXA{sfK{]dQLPmG@/i@#LKNMdN/Rk#?O^tLZ2^x0SvI.f{oOc}F#.[e3%8.+eL)Ei4Y=>EHnu2KaORi-cr?^<&zggVE=ec50P@m8{ynmvSJ3=%)}I=n4Z!v/2zqLpaZVse40D$6/0w/nQJB*[^Q9s!bhmR+hmjA).M!=dSsz>p)qYqm-NkWJ32yAqN6e97vKU:neI9YJ$wQ=y+06r&C%@-dyWHr[}}/wu1GW/L)c-Zl$Jy-S!0jiutubKUeWCPyk#F)CbYMFNETuH(yi@uj*0:9rgy3x^@]IdWd^:=Mz25Ae9Y>4uIW=LZp(($S9!eG>>qPG9&ZQK}t9tifSD7eJx$N2w214$Xm@Pk0/ZgtGyNL8w^bJP*1L!5JY4Yxji+F3dF5C):Af9r+Xe/&@biGOzl6c-Z-41kV@Qp#T]l*9(X]#7DtIm=CpQQn9VaH/mEGS/t]#btC(RHOZ8vV(PnG#/&ZI]O[3WXxush8:8X*]XOV]+5EAoU-03/tGp8@rF08rc]^4J:E:27X/eTor5@t!a>vl}eH6s:ZZ5Mc))32CFU^LXr!HjdP}x&ljq&7CrA9BYwDyv^M]*{vCFj/K$i.d[f[iUGhEKnb+1x)IgeS.R%5XPx@HB4]QNKO*4=t&)XHHp*ve?ppKz>n2q5IgFLhH5gQxl>Fhi9vQ2M^bu%9berlqz9KlgI]JNK7qz*#f$f0Vxdw]p9M7?W3cfJG?)d$IEb6x$W7T/eraF]1LLfc6q*m5nPPE(u=&F6aZojv.!:eIO87DJ.N1M>pAM3EZulVy2]IwdvZewonyPv!g3qwK2nOVglum]>GpeDP>D&(U&:RC&:}^Yi9YgX/!rS)*@^#O@XYm?$E(X>UaLerTS>WYL-Hld5Qgj+DDA4CCxT}zH//E.W1RANPmT/Z[m(&0N*-G.u]BOSiN:&((a<[9z-d?9.!KEOQi-hK/wtwrUmakGP{+W3E>&O+M9ZWglA!iv).kqSu@H?:*ts&hqVa7>G+Xa.cz}Sig-z&I[38Lvvv+F4>/sYj]Z}gJCFO0(y*IpHrPfzvwdQyJ0D!3P%8VaznP!W]ji-c+U^NG/*vIpbffC2DQaC5<9lN8&}6WmxXT*$ni%4KnM7{I:UQRs:]CQ8r6oNQtl$iLiyMKj8D>fI6t83Y>h1{zu:=LIk6w&(}MpFAChxm08-bg:0ATAD!$%MkbvgZY#OiPRdQMJU7pQb+@jxL78#?k)%GY@3K.*s4&wX[p[UKlc!iv!oB3/ffvGNqhg4[Q9kPt}}rl&-pE]]MYo:}b]QlM1dgtI1CS4W:)k@!Pn98H<]7Q*83WUWpUS@57c@jAF>0IUsW0!WV^E>f6&ayVC>HLR)E{9All#QFt1p1{P$7k19lZSfEoIz@lTZ}Xij5o{4K[17!*He3NSEp3>H#C5C?P&q!C@%(*C1g566+tV-BrL)%mU[x5)cFS]dD0-RZp!0Su*N0):c@]K+mN2^19x}(#l)!x@uQnq7*nVsxOWw.n{*xq8ahI(HrziMVtX$ni)}*KxF}w}=Y)TB^?J2?oS4v}zS4ISt.HbOvEazo{DtXt{gX26z1iD9@CWm(jhksnyvqlN}!L9G)D8H9ATthB4kPQ1SjL!^VwSC:zxLXPx*i)OFwm5d:.tNjG-R)8+gEuBVCvIO!(STKUD>B[}u(9SyPBEHKO&X)A0EHlABe=Y}7k5i>.OezM@xu>g{QTdH2m-8bX+=nUmFhB+8&#+VOGaOj0MC2{kqmRYjWtr(kOtFhDmfD+v*rELVv9iozCvLzFgTGM9N?JLUkX+cVry6D{JLn:ZLy0sWF6?4-a7w1}5:X(jA#}q.]@LAZPsWs^@Cz)=kQ/=!:ZmeEkRP=-DCoDbpAM0.7z4.]kSP(F%OclX%=k/4OZT/9*sJ6#c+!c8cC5Kt8%JlX.wPIi+yQks%mH(I@oja!xyDmNwF[OgKHP6fQ}jXvw{V$I?cN:!kYb})-35D6PI$lq-K9BPHY1P@L]hk]N(v)F7R0gizbdj44!mzrdHN!N:Ns8r+Y[X2YV60Barvib^JLfjMQW!C26EFsVIIGS6SDo+Q}UR1oSw+whsQuVm.gstdvjsv4bCJ-FHS10=CE=fxJ5PqTSj{OhuR=ecy{b$@CD0g-P]/9YyY:jd$.yk-Dc>d:{Q[+=5Q}@*FQbalCEAErm9iC1jV*0BAgc@cQYgS4%91WS/+FzoFXN?z7r@&9+iz}>Vfn2MI>Kw&pYiMdcH3vmJ-CGM{m(B>7Efw#5kUihn8m(qc5Ici]ge<}mrs:e0BRSn8a4:/uI2Y4=BA-q^*47?L>rjxz]=@eH6EcYgQC:f/{V1Iy^.I(Ycn]kLljbG]k)*tR%&@IDV]BYz[BP6QEa0&qA(uzDB6gj[A1GY$PiM8I&3?qd>6RvP[5s<>mtnqiaLJ+RI]+c:Cl#3CrVeFlK3Izoqt(1=W4F1#9yNJI..m)}VtI)%*daUV8c>%)+]VuZUW7>GZowhN*T$lkj2>/jCa0z)ZIIX}%7}OKi.I7HUIZZvkA7/J}c7A-(:v[-un[#wEmAM%7K{SbE=ErI^K!XjzqQF*gV!TxYhNhbgXQg>XbTmWlX^F7Vu76T[^w#Z[$Lcb^Z5C3:%$w)QSny!ic><:>m=Z})@}rETxX9@g6}1TaBM-]qX%m(AQ{N/k2NWNC:*Ua.IYsJunL}6gh(ZgMc2hC/AmQP#{6SO{d($F})UD?mj0i&GWu+Vu@^f(e53jW=$9&}6MY?AZuQHeVxo+k[@CT2hh.&Z#gn8Dz1M%DP4.G?(O1N!D6z^mY[Y]j!%6I!OTOz#%#K2-sJ1E?^-:QK5Ma^p$NhTSt@hTKsc!S-1*DpuP1EU.j/O{hVPH.)/@&l)Sy0UYN]qu}X!S7l-O9UimCK2Ec7K^=*7/U0w@e390w1yc2Dc&./*%5o5yRdpa=pDs&&.R?N2&SJn=5K)WIV[XfUa!=+VG#Uy%o-BXf/XOQFo@JOH2$Nuaw86OtZ}REE{BD$HsjmZr2qfdtux-aFRWAHZEDW^lCN%iE@z[ZH16sQDt3uK7Ef]#xOuVM[!#.H%ftEO$n0=HQ(5E$zZvEBVWejpev.v$E]9XvauE^{U=bJgMJwC4IS}Ckv*C?xMF&x{z=P&5tPHjG-FraR.]G>m<%k2:>p:#wgSO^=as{s1=az$X(dj#p8CA*zZ{!2BG2oUXmGUyl6r}26L#J0G&Y.Rk{:6iJr$y=vljOfw+4G-OZ4gv&klH+1R$}!aR1{&{^v21A1AEt/]J@/G#]/uy]cdw44NUgjoyY%qt7ss(g!}@kcdU*P!{f0YQSB5gAv]T?Nrm0{TBhHH/v&9C(NUg)-L{bnH$jm(c*&(C*LZWY^*4e)ieVcd%XQ8y0f*H)tTIPcK]&.:P2kvJ+2@Te#!IDhF75Vb#70KS=5qe#q=(?uoXusYhsLM=ziRVFwH*HT(8ABrt07xkek@-6*gj2w?5V8bv}hYImi56e>z@Wa7dT+o{VLau8^DT{MZA)LrFmAO>^R4tKhxTmGY[)0WOE6eg:djKotgNx)lCFPHY70f/K<]y7.-Tg/XT4v:uO4QGAC1g3B4S?.V2O3R&8GlXZH.tm{42wgp{krA[pQR%wh*GijX[#NqyTdP7xAHS[[a)HI0+J5i9aoqDx', + 219247, +); +export const compiled = await WebAssembly.compile(new Uint8Array(brotliDecompressSync(compressed))); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b62d3ca..79b38a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -304,7 +304,7 @@ packages: engines: {node: '>= 20.12.0'} '@clack/ui@https://pkg.pr.new/bombshell-dev/ui/@clack/ui@a60ac12': - resolution: {tarball: https://pkg.pr.new/bombshell-dev/ui/@clack/ui@a60ac12} + resolution: {tarball: https://pkg.pr.new/bombshell-dev/ui/@clack/ui@a60ac12, integrity: sha512-RuGTYiIwK+nsC7WxBdPm3636wUJtfyvzPoPWpzXkeV3KgCASLlsF3Y5ZG/UN2yWviceDHFu9Ou15JUBs3VJEjw==} version: 0.0.0 '@emnapi/core@1.10.0': From 25aa0d8530419beec43de6b97999358be446b7a1 Mon Sep 17 00:00:00 2001 From: Nate Moore Date: Wed, 21 Oct 2026 22:56:02 -0400 Subject: [PATCH 9/9] Resolve light-dark() against the color scheme; canonicalize var() substitution MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `light-dark(a, b)` no longer folds to its light branch at compile time. Both branches are validated and canonicalized as standalone colors, the call is preserved through the IR, and the runtime resolves it at computed-value time against the environment's color scheme: after var() substitution (a custom property may carry the call verbatim into the using declaration) and before descendants inherit the value. A malformed call — anything other than exactly two colors — is invalid at computed-value time and drops the declaration, like a failed var() resolution. Resolution is scoped to the color properties the compiler canonicalizes via a shared `is_color_property` predicate in css-core. The compiler's sheet-level path rejects malformed calls with the same "invalid color value" diagnostic the plain color path reports. Also fix var() substitution to always re-serialize through the token writer: custom-property values arrive verbatim, and the old early return leaked uncanonicalized numbers (`oklch(70% 0.2 40)` stayed `0.2` where the conformance corpus pins `.2`). Drop the unused `Stylesheet::layer_rank` method. Rebuild the embedded wasm artifact; extend the compiler and runtime tests and the smoke test with light-dark coverage under both schemes. --- .../css/crates/css-compiler/src/canonical.rs | 120 ++++++++---- .../css/crates/css-compiler/tests/compiler.rs | 43 ++++ experiments/css/crates/css-core/src/lib.rs | 7 + .../css/crates/css-runtime/src/cascade.rs | 42 +++- .../css/crates/css-runtime/src/values.rs | 185 +++++++++++++++++- .../css/crates/css-runtime/tests/runtime.rs | 96 ++++++++- experiments/css/smoke.mjs | 42 ++++ experiments/css/src/wasm.js | 4 +- 8 files changed, 491 insertions(+), 48 deletions(-) diff --git a/experiments/css/crates/css-compiler/src/canonical.rs b/experiments/css/crates/css-compiler/src/canonical.rs index 12edfa9..fcc5883 100644 --- a/experiments/css/crates/css-compiler/src/canonical.rs +++ b/experiments/css/crates/css-compiler/src/canonical.rs @@ -6,7 +6,9 @@ //! leading/trailing zeros (`0.2` → `.2`), and color-property values are //! validated (a color is a single component: keyword, hash, or function) //! so invalid declarations report through the recovery paths like they used -//! to. +//! to. `light-dark(a, b)` is preserved (branches canonicalized) for the +//! runtime, which resolves it against the color scheme at computed-value +//! time. use bombshell_css_core::Diagnostic; use bombshell_css_core::tokens::write_token; @@ -30,28 +32,26 @@ pub(crate) fn canonicalize(name: &str, value: &str) -> Result {} } - if name == "color" || name.ends_with("-color") { - // `light-dark(a, b)` resolves to its light branch at compile time - // (the previous front-end's printer behavior). - let value = match resolve_light_dark(value) { - Some(light) => light, - None => value.to_string(), - }; - - validate_color(&value)?; - - // The shortest representation wins; ties go to hex (the previous - // front-end's printer behavior: `red` stays, `yellow` → `#ff0`). - let trimmed = value.trim(); - if let Some(hex) = named_color(trimmed.to_ascii_lowercase().as_str()) { - return Ok(if trimmed.len() < hex.len() { - trimmed.to_ascii_lowercase() - } else { - hex.to_string() - }); + if bombshell_css_core::is_color_property(name) { + // `light-dark(a, b)` survives to the runtime, which resolves it + // against the environment's color scheme at computed-value time. + // Both branches are validated and canonicalized here so the IR only + // ever carries canonical color text. + if is_light_dark_value(value) { + let Some((light, dark)) = split_light_dark(value) else { + return Err(invalid_color(value)); + }; + validate_color(light.trim())?; + validate_color(dark.trim())?; + return Ok(minify_value(&format!( + "light-dark({}, {})", + canonical_color(light.trim()), + canonical_color(dark.trim()), + ))); } - return Ok(minify_value(&value)); + validate_color(value)?; + return Ok(canonical_color(value)); } if name == "font-weight" { @@ -65,12 +65,51 @@ pub(crate) fn canonicalize(name: &str, value: &str) -> Result Option { +/// Canonical color text: the shortest representation wins and ties go to +/// hex (the previous front-end's printer behavior: `red` stays, `yellow` → +/// `#ff0`). +fn canonical_color(value: &str) -> String { let trimmed = value.trim(); - let rest = trimmed.strip_prefix("light-dark(")?; - let rest = rest.strip_suffix(')')?; + if let Some(hex) = named_color(trimmed.to_ascii_lowercase().as_str()) { + return if trimmed.len() < hex.len() { + trimmed.to_ascii_lowercase() + } else { + hex.to_string() + }; + } + minify_value(trimmed) +} +/// The shared diagnostic for a color value that does not parse. +fn invalid_color(value: &str) -> Diagnostic { + Diagnostic::warning( + "declaration", + { + let mut message = String::from("invalid color value `"); + message.push_str(value); + message.push('`'); + message + }, + ) +} + +/// `light-dark(a, b)` → its two branches, split at the top-level comma. +/// Function names are case-insensitive; anything that is not exactly the +/// two-argument form returns `None` (the caller reports it invalid). +fn split_light_dark(value: &str) -> Option<(String, String)> { + let trimmed = value.trim(); + if !trimmed[..trimmed.len().min("light-dark(".len())] + .eq_ignore_ascii_case("light-dark(") + { + return None; + } + let rest = &trimmed["light-dark(".len()..]; + if !rest.ends_with(')') { + return None; + } + let rest = &rest[..rest.len() - 1]; + + let mut split: Option = None; let mut depth = 0usize; let mut quote: Option = None; for (i, &b) in rest.as_bytes().iter().enumerate() { @@ -81,13 +120,30 @@ fn resolve_light_dark(value: &str) -> Option { b'"' | b'\'' => quote = Some(b), b'(' => depth += 1, b')' => depth = depth.saturating_sub(1), - b',' if depth == 0 => return Some(rest[..i].trim().to_string()), + b',' if depth == 0 => { + if split.is_some() { + // A second top-level comma is not the two-argument + // form. + return None; + } + split = Some(i); + } _ => {} }, } } - None + let comma = split?; + let (light, dark) = (&rest[..comma], &rest[comma + 1..]); + Some((light.trim().to_string(), dark.trim().to_string())) +} + +/// Whether the value is a `light-dark(...)` function call (case-insensitive +/// name, balanced parens). +fn is_light_dark_value(value: &str) -> bool { + let trimmed = value.trim(); + let lower = trimmed.to_ascii_lowercase(); + lower.starts_with("light-dark(") && trimmed.ends_with(')') } /// A color value is one component: a keyword, a hash, or a balanced @@ -169,15 +225,7 @@ fn validate_color(value: &str) -> Result<(), Diagnostic> { if valid { Ok(()) } else { - Err(Diagnostic::warning( - "declaration", - { - let mut message = String::from("invalid color value `"); - message.push_str(value); - message.push('`'); - message - }, - )) + Err(invalid_color(value)) } } diff --git a/experiments/css/crates/css-compiler/tests/compiler.rs b/experiments/css/crates/css-compiler/tests/compiler.rs index 818aff4..90b38c2 100644 --- a/experiments/css/crates/css-compiler/tests/compiler.rs +++ b/experiments/css/crates/css-compiler/tests/compiler.rs @@ -123,3 +123,46 @@ fn ir_round_trips_through_compiler() { assert_eq!(decoded.rules.len(), 2); assert_eq!(decoded.rules[1].selector, ".a:hover"); } + +#[test] +fn preserves_light_dark_for_the_runtime() { + // The compiler no longer folds to the light branch: both branches are + // validated and canonicalized, and resolution happens against the + // environment's color scheme at computed-value time. + let sheet = compile_ok(".card { color: light-dark(white, black) }"); + assert_eq!(sheet.rules[0].declarations[0].value, "light-dark(#fff, #000)"); +} + +#[test] +fn canonicalizes_light_dark_branches_independently() { + // Case-insensitive function name; each branch canonicalizes like a + // standalone color value. + let sheet = compile_ok(".card { border-color: LIGHT-DARK( #ffffff , yellow ) }"); + assert_eq!( + sheet.rules[0].declarations[0].value, + "light-dark(#ffffff, #ff0)" + ); +} + +#[test] +fn rejects_malformed_light_dark() { + for css in [ + ".card { color: light-dark(#fff) }", + ".card { color: light-dark(#fff, #000, #888) }", + ".card { color: light-dark(#fff, nope) }", + ] { + let out = compile(css, Origin::Author, Default::default()); + let sheet = out.sheet.expect("recovered sheet"); + assert!( + out.diagnostics + .iter() + .any(|d| d.message.contains("invalid color value")), + "missing diagnostic for {css}: {:?}", + out.diagnostics + ); + assert!( + sheet.rules.iter().all(|rule| rule.declarations.is_empty()), + "declaration survived for {css}" + ); + } +} diff --git a/experiments/css/crates/css-core/src/lib.rs b/experiments/css/crates/css-core/src/lib.rs index 1b8f15f..3980c9b 100644 --- a/experiments/css/crates/css-core/src/lib.rs +++ b/experiments/css/crates/css-core/src/lib.rs @@ -221,6 +221,13 @@ pub struct Declaration { pub important: bool, } +/// Whether `name` takes `` values. Both the compiler's color +/// canonicalization and the runtime's `light-dark()` resolution key off this +/// predicate, so it lives here next to the declaration type. +pub fn is_color_property(name: &str) -> bool { + name == "color" || name.ends_with("-color") +} + /// A preserved `@keyframes` block. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] pub struct KeyframesRule { diff --git a/experiments/css/crates/css-runtime/src/cascade.rs b/experiments/css/crates/css-runtime/src/cascade.rs index f25b7a2..9eaece6 100644 --- a/experiments/css/crates/css-runtime/src/cascade.rs +++ b/experiments/css/crates/css-runtime/src/cascade.rs @@ -8,7 +8,7 @@ use crate::conditions::{ ContainerAncestor, ContainerType, eval_container_list, eval_media_list, eval_supports_list, }; use crate::selectors::parse_selector_list; -use crate::values::resolve_vars; +use crate::values::{resolve_light_dark, resolve_vars}; use bombshell_css_core::{CompiledStylesheet, ContainerQuery, Declaration, Origin, TreeSnapshot}; use rustc_hash::FxHashMap as HashMap; @@ -222,10 +222,6 @@ impl<'ir> Stylesheet<'ir> { .iter() .map(|d| &d.message["selector could not be matched at runtime: ".len()..]) } - - fn layer_rank(&self, layer: Option<&[String]>) -> u32 { - layer_rank(self.layer_order, layer) - } } @@ -385,6 +381,10 @@ pub fn compute_tree<'ir>( // var() substitution is part of computing the value: descendants // inherit computed values, not raw `var()` text. resolve_vars_in_map(&mut map); + // `light-dark()` resolves at computed-value time, after var() + // substitution (a custom property may carry it into the using + // declaration) and before descendants inherit the value. + resolve_light_dark_in_map(&mut map, env.color_scheme); maps.push(Some(map)); } @@ -427,6 +427,38 @@ fn resolve_vars_in_map(map: &mut StyleMap<'_>) { } } +/// Resolve `light-dark()` in every non-custom color declaration against the +/// environment's color scheme. Resolution is scoped to the color properties +/// the compiler canonicalizes; custom properties substitute verbatim and +/// resolve at the using declaration. A malformed call drops the +/// declaration, like a failed `var()` resolution. +fn resolve_light_dark_in_map(map: &mut StyleMap<'_>, scheme: bombshell_css_core::ColorScheme) { + let names: Vec> = map.keys().cloned().collect(); + for name in names { + if name.starts_with("--") { + continue; + } + if !bombshell_css_core::is_color_property(name.as_ref()) { + continue; + } + if let Some(value) = map.get(name.as_ref()) { + if value.contains("light-dark(") { + match resolve_light_dark(value, scheme) { + Some(resolved) => { + if resolved != value.as_ref() { + map.insert(name, Cow::Owned(resolved)); + } + } + None => { + // Invalid at computed-value time. + map.remove(name.as_ref()); + } + } + } + } + } +} + /// Handle `inherit`, `initial`, `unset`, and `revert` keywords. fn apply_css_wide_keywords<'ir>(map: &mut StyleMap<'ir>, inherited: &StyleMap<'ir>) { let names: Vec> = map.keys().cloned().collect(); diff --git a/experiments/css/crates/css-runtime/src/values.rs b/experiments/css/crates/css-runtime/src/values.rs index 3e1a789..32f3188 100644 --- a/experiments/css/crates/css-runtime/src/values.rs +++ b/experiments/css/crates/css-runtime/src/values.rs @@ -1,4 +1,5 @@ -//! `var()` substitution against resolved custom properties. +//! `var()` substitution against resolved custom properties, and +//! `light-dark()` resolution against the environment's color scheme. //! //! Values are canonical CSS text; substitution walks the token stream so //! strings and nested functions are handled correctly. Cycles resolve to @@ -6,6 +7,7 @@ //! caller falls back to inheritance). use bombshell_css_core::tokens::write_token; +use bombshell_css_core::ColorScheme; use cssparser::{Parser as CssParser, ParserInput, Token}; use crate::cascade::StyleMap; @@ -31,10 +33,11 @@ fn resolve_value( if depth > 32 { return None; } - if !value.contains("var(") { - return Some(value.to_string()); - } + // Every value this function returns is canonical: the token walk + // re-serializes numbers and whitespace even when no `var(` remains + // (custom-property values arrive verbatim and must still come out + // canonical — the conformance corpus pins that). let mut input = ParserInput::new(value); let mut parser = CssParser::new(&mut input); let mut out = String::new(); @@ -231,3 +234,177 @@ fn write_tokens( } } } + +/// Resolve every `light-dark(light, dark)` in a color value against the +/// environment's color scheme, returning the computed value. `None` marks +/// the value invalid at computed-value time (a malformed call — anything +/// other than exactly two arguments). +/// +/// Runs after `var()` substitution: a custom property may carry +/// `light-dark()` verbatim into the using declaration. Branches are emitted +/// verbatim — the compiler canonicalized stylesheet branches, and inline +/// styles pass through like every other inline value. +pub fn resolve_light_dark(value: &str, scheme: ColorScheme) -> Option { + if !value.contains("light-dark(") { + return Some(value.to_string()); + } + let mut input = ParserInput::new(value); + let mut parser = CssParser::new(&mut input); + let mut out = String::with_capacity(value.len()); + write_light_dark_tokens(&mut parser, &mut out, scheme)?; + Some(out) +} + +/// Token walk that rewrites `light-dark()` functions in place; other +/// functions and parens recurse without interpretation. `None` marks a +/// malformed `light-dark()` anywhere in the value. +fn write_light_dark_tokens( + parser: &mut CssParser<'_, '_>, + out: &mut String, + scheme: ColorScheme, +) -> Option<()> { + let mut malformed = false; + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Function(name) if name.eq_ignore_ascii_case("light-dark") => { + match parse_light_dark_args(parser) { + Some((light, dark)) => { + out.push_str(match scheme { + ColorScheme::Light => light.trim(), + ColorScheme::Dark => dark.trim(), + }); + } + None => malformed = true, + } + } + Token::Function(name) => { + out.push_str(name); + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + if write_light_dark_tokens(parser, out, scheme).is_none() { + malformed = true; + } + Ok(()) + }, + ); + out.push(')'); + } + Token::ParenthesisBlock => { + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + if write_light_dark_tokens(parser, out, scheme).is_none() { + malformed = true; + } + Ok(()) + }, + ); + out.push(')'); + } + other => write_token(out, &other), + }, + Err(_) => break, + } + } + if malformed { + return None; + } + Some(()) +} + +/// Read the two color arguments of `light-dark(...)`. The contents are +/// serialized verbatim; a second top-level comma, an empty branch, or a +/// non-paren block makes the call malformed. +fn parse_light_dark_args(parser: &mut CssParser<'_, '_>) -> Option<(String, String)> { + let mut light = String::new(); + let mut dark = String::new(); + let mut seen_comma = false; + let mut malformed = false; + let _ = parser.parse_nested_block(|parser| -> Result<(), cssparser::ParseError<'_, ()>> { + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Comma => { + if seen_comma { + malformed = true; + } + seen_comma = true; + } + Token::Function(name) => { + let branch = if seen_comma { &mut dark } else { &mut light }; + branch.push_str(name); + branch.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_branch_tokens(parser, branch); + Ok(()) + }, + ); + branch.push(')'); + } + Token::ParenthesisBlock => { + let branch = if seen_comma { &mut dark } else { &mut light }; + branch.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_branch_tokens(parser, branch); + Ok(()) + }, + ); + branch.push(')'); + } + // Square/curly blocks cannot appear inside a color. + Token::SquareBracketBlock | Token::CurlyBracketBlock => { + malformed = true; + } + other => { + let branch = if seen_comma { &mut dark } else { &mut light }; + write_token(branch, &other); + } + }, + Err(_) => break, + } + } + Ok(()) + }); + if malformed || !seen_comma || light.trim().is_empty() || dark.trim().is_empty() { + return None; + } + Some((light, dark)) +} + +/// Verbatim token serialization for `light-dark()` branches: no `var()` +/// resolution (substitution already ran) and no rewriting. +fn write_branch_tokens(parser: &mut CssParser<'_, '_>, out: &mut String) { + loop { + match parser.next_including_whitespace() { + Ok(token) => match token { + Token::Function(name) => { + out.push_str(name); + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_branch_tokens(parser, out); + Ok(()) + }, + ); + out.push(')'); + } + Token::ParenthesisBlock => { + out.push('('); + let _ = parser.parse_nested_block( + |parser| -> Result<(), cssparser::ParseError<'_, ()>> { + write_branch_tokens(parser, out); + Ok(()) + }, + ); + out.push(')'); + } + other => write_token(out, &other), + }, + Err(_) => break, + } + } +} diff --git a/experiments/css/crates/css-runtime/tests/runtime.rs b/experiments/css/crates/css-runtime/tests/runtime.rs index 1b94a62..c9de400 100644 --- a/experiments/css/crates/css-runtime/tests/runtime.rs +++ b/experiments/css/crates/css-runtime/tests/runtime.rs @@ -642,6 +642,100 @@ fn dark_scheme_and_state_via_nesting() { ..Default::default() }; let s = compute(std::slice::from_ref(&&sheet), &t, 0, &env).unwrap(); - // :hover and dark-mode rules both match; dark rule is later in source order. + // Both rules match, but `:hover` carries higher specificity (0,2,0 vs + // 0,1,0), so the hover rule's plain `white` wins regardless of what the + // dark rule's `light-dark()` would resolve to. assert_eq!(get(&s, "color"), "#fff"); } + +#[test] +fn light_dark_resolves_against_the_color_scheme() { + let case = stylesheet(".card { color: light-dark(white, black) }"); + let sheet = case.sheet(); + let mut n = node("card"); + n.classes = vec!["card".into()]; + let t = tree(vec![n], vec![NO_PARENT]); + + let light = Environment::default(); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &light).unwrap(); + assert_eq!(get(&s, "color"), "#fff"); + + let dark = Environment { + color_scheme: bombshell_css_core::ColorScheme::Dark, + ..Default::default() + }; + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &dark).unwrap(); + assert_eq!(get(&s, "color"), "#000"); +} + +#[test] +fn light_dark_resolves_through_var_substitution() { + // A custom property carries `light-dark()` verbatim (custom property + // values are not canonicalized); the using declaration resolves it at + // computed-value time, picking the dark branch as authored. + let case = stylesheet(":root { --panel: light-dark(white, black) } .card { color: var(--panel) }"); + let sheet = case.sheet(); + let t = tree( + vec![node("root"), { + let mut n = node("card"); + n.classes = vec!["card".into()]; + n + }], + vec![NO_PARENT, 0], + ); + let dark = Environment { + color_scheme: bombshell_css_core::ColorScheme::Dark, + ..Default::default() + }; + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &dark).unwrap(); + assert_eq!(get(&s, "color"), "black"); +} + +#[test] +fn light_dark_inherits_as_a_computed_value() { + // Descendants inherit the resolved branch, not the raw function text. + let case = stylesheet(".card { color: light-dark(#123456, #654321) }"); + let sheet = case.sheet(); + let t = tree( + vec![{ + let mut n = node("card"); + n.classes = vec!["card".into()]; + n + }, node("title")], + vec![NO_PARENT, 0], + ); + let dark = Environment { + color_scheme: bombshell_css_core::ColorScheme::Dark, + ..Default::default() + }; + let s = compute(std::slice::from_ref(&&sheet), &t, 1, &dark).unwrap(); + assert_eq!(get(&s, "color"), "#654321"); +} + +#[test] +fn malformed_light_dark_drops_the_declaration() { + // Inline styles pass through un-canonicalized, so a malformed call only + // surfaces at computed-value time; it drops the declaration like a + // failed var() resolution. + let case = stylesheet(".card { color: blue }"); + let sheet = case.sheet(); + let mut n = node("card"); + n.classes = vec!["card".into()]; + n.style = Some("color: light-dark(#fff)".into()); + let t = tree(vec![n], vec![NO_PARENT]); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &Environment::default()).unwrap(); + assert_eq!(s.get("color"), None, "malformed light-dark must drop"); +} + +#[test] +fn light_dark_stays_verbatim_outside_color_properties() { + // Resolution is scoped to the color properties the compiler + // canonicalizes; other properties keep the value as authored. + let case = stylesheet(".card { border: light-dark(#fff, #000) }"); + let sheet = case.sheet(); + let mut n = node("card"); + n.classes = vec!["card".into()]; + let t = tree(vec![n], vec![NO_PARENT]); + let s = compute(std::slice::from_ref(&&sheet), &t, 0, &Environment::default()).unwrap(); + assert_eq!(get(&s, "border"), "light-dark(#fff, #000)"); +} diff --git a/experiments/css/smoke.mjs b/experiments/css/smoke.mjs index 84650f8..3649bbb 100644 --- a/experiments/css/smoke.mjs +++ b/experiments/css/smoke.mjs @@ -117,6 +117,48 @@ assert.ok( ); warned.release(); +// light-dark() survives compilation (branches canonicalized) and resolves +// against the environment's color scheme at computed-value time. +const schemeSheet = compile('.card { color: light-dark(white, black) }', 2); +const schemeTree = [{ tag: 'card', classes: ['card'] }]; +const schemeParents = [NO_PARENT]; +assertColor( + colorOf( + schemeSheet.compute(schemeTree, { + parents: schemeParents, + target: 0, + env: { ...env, colorScheme: 'light' }, + }), + ), + { + expected: '#fff', + label: 'light-dark under a light scheme', + }, +); +assertColor( + colorOf( + schemeSheet.compute(schemeTree, { + parents: schemeParents, + target: 0, + env: { ...env, colorScheme: 'dark' }, + }), + ), + { + expected: '#000', + label: 'light-dark under a dark scheme', + }, +); +// A malformed call is invalid at computed-value time: the declaration drops. +const malformedScheme = compile('.card { color: blue }', 2); +const malformedNode = { tag: 'card', classes: ['card'], style: 'color: light-dark(#fff)' }; +assert.strictEqual( + colorOf(malformedScheme.compute([malformedNode], { parents: schemeParents, target: 0, env })), + null, + 'malformed light-dark must drop the declaration', +); +schemeSheet.release(); +malformedScheme.release(); + // Disposable protocol: release is synchronous, so `using` works via // Symbol.dispose and `await using` via Symbol.asyncDispose. (The `using` // syntax itself is exercised in smoke.using.mjs on Node 24+/bun.) diff --git a/experiments/css/src/wasm.js b/experiments/css/src/wasm.js index 369f795..7262623 100644 --- a/experiments/css/src/wasm.js +++ b/experiments/css/src/wasm.js @@ -22,7 +22,7 @@ function d(s, n) { return b; } const compressed = d( - 'uV5$!QnS7vDk01b5fMp4tdQ*XREn+pA657i-{we?i1..Uv[0vQ:FFgBW=ImWS&zIMay+*OZ$}oS6(xI@427RtT?r?eT+tcm]+F+V]r1Qcp.*bdn]4<<{DU?:?JQR-EvO]{ADYV7ehNGKg)odcq@p&Oh#>w:MWiu31%mbFXCaF=7!RTi=6]RIkrIbS}1TaYJPi(^vq$LZdBpduom-=JyjY9horYjk-9G[B-bv4^W-vS4!M8wvllH}nyw$VYR#!cX81}rb9J}g%4vWxxVdk2Q#m>Q^)E!%9h7WlRUa7/+OrL*w[i#+#z.u8vFyYIIfL=mG!jYLHTb?-eVkES(K?toasn0RiJSqZm*DW[K5FqE.n2adKY}atr/J$BmAUT>*LIK0Tyr])x.a%Kv[-I^7#gzFn4}}+FuMo*OP@}Dx*a-OTeBJYRIVX[0Y}*Le{S]u6/Ca[/#Zp)ZO/OyPQbfFa7Pwf1vL]kXMg!s88wmuYtb5=#+NtZCsrqDX-cntZgGeAJaE9HiP{UxiHS{mpS0u.8kkJ9tYIFN-Cy!qNvM$oNVr^JB$a5TwqPYg+L):U/?7#6+(impA*IBeF9fe.FQGE0y62d[(R)XBu.ao->!>k5s{a:T9OIQ$)m8h)MW--r8X=6Y?ZOKKauG}UkjG1TuvR0Sen1vF]/oUe^U:k3bu=FAa9SyV/1x/1id121dM:-sv[rp(/hH}1YspJL47N&iLIT?Zj2}FhtaX[9+}>ehW%QTsHyeQ!DXt1llz9e)KxRrQ1v3eu@mg#9]3A#mT/2r:ORW!ON/pFi8{qzPO7]3N#Q.b9V9K@zKD810QM(FLcayp-tnO{&*to)VLkg&=XVoS0PRmT3!D?dgoX6*fOt1[LW8h*z-#=9Ec/qE7oD0?K*/vlc8+IjI05ob]CKtn<-r5b1}QvS>9L](!9>uD6^F?nW-n6JBslV.v87d+rDxpaB9b/xJ?x<4UQ+?)7:>LL8riCMydQe1JpS{Hd1Tq!Cl^etE3m:36=Fgp3Aly(*8PZC&Ac@{]epg-2q9&Faw{k0}YI]Q8hZ8$q[klglNad%C.08&>CRgCq7cp6Y{PQV4kC7L%Gp0g:j^v5VZ4%r{0+c50tIfot>D*]xHRHa*HT3d]ishM>hD:h?%fUamE-KlPa?T$HhI=zAN}@W.ipGzW)M+(Ud!&y:9F3F}1g><]b7.5S7AqjjLZpNO:4S(d@]&6}j4^:4o=)1f)W*[Pa^GfK1H#%&CS>FA%}:(/mE<-7z}!0BGSxCOmiM[=)]hx-Bg8Qf8@guiCzNqT$1p3=o(h3&XOOT+q)Xo^x:v^UJak>:k*yGIEvhj-#o/A{]/Y%T5Am%#!gEju-1kO)>Kep.&p1nrs}zOH%3]2FEnq{&rP&$2=X4-cc4tVg6LJ4L}.(OuQQQbsEQCkQT5FkSL7?OkhYy&P%o]P7c2-o$7rSg.u:CWIK!?&4R:U2@*Zal+>{i8f[lp.fg+=(@5nU4!%Ct&}:z(VXG-zP0fv5L{BO?oS8LQly%}>JMC=mxg44kW:jR$K>98C:tpxlM^%5tr/S{}0FHJxig}ZGZSU9$!OMS9ux0c&xbP@l?H(IkK.y]X%@{d/=P^*!%Xv8wCTSwxIS}uxfl}?iq+Q{[]lv{}j710ntSmK4Lg6BEZmz-OAyWmK&7}<7BWo/&Kon#KELh+Z[ZJ=[b)(UAf1F5(M!PyN=c0rM{#zJUcuZKdxAvfuQKnr&ntl]daMKR-}/8[3AoueQsVAEke!65pw!0$l.R4a]GMK!7Y?6RdF>PbN(C^k1EkF1Egn5NV7>8<2!(bMOD[>G)]&]u?bd4FG9ZLl@Q4LH^3K*2NMX1s{ZHuJ=lej56+EcX{$eNqIFTw?#PldF0=t%bc>&=TvNI]a#ARw)SZHx4l5nXry5juUZG{ap#:t7Pwzmm1:AN0lhIO030$iNgtB!VN.=$^?2F=S=2(F/fA>maJJw&T1dCQB%@=SoRw4z5A>nrrWAp#fXV[9Vol*C)s=M7rX?PyW1o2&p./:.}U1ApV(WypWr#Z{W?R!%]kCEh-vXe!rK{Q}X6lq%kK%fP(UY4e-cAK?wS(m}kUb5sQMinT4Syy5A){dM-OlgP1=E)lHZi3-XikxH-M6z<34d@Xt]hDlrb9ARkOvQ:*y@MgkPXC#Pqf^R5Ga(A[/]!7xTX>ttV6M3d9j5E])F)9a7).N.?6zC@Jr:#P&vKY$%WVUGrjjh$bQYRA%pfZaTnF@%86@m0(5D$fYJ/[c$OsyYH:TIn0OfqK!iDyY@aqWPUSu.8iMnBY7]xQ/*ChlJ>v6CtX0esChi79:05CpCKC:75W*#%}lgXXggSh{jDc=450w=G?Sye?HYy-p?)y8StL)RSYWy8E@TZnM%$j&1}iQu.[UmjP90ohtv6mQod@)8G49u^@B>*dpZyhY3D&6Ww>j:josB6h:]a6%fFyK-FU{1Tz+b=n*caOtXcc%yM]S3w8EIC>Ip@6R-Sud6J{UsS*3gD+3l}-Rt.u^Pd:^u:KL}R.j!U@:M{s2Gv^ZVH9s$U@a<=/9yXkRK=#^+sTS>RE/8CTq2U[/e%4S@?P)dR1qO]OHfAwr6T05C:H16jq-7=Fd!9N-y>6Aqe6e7+&.R8c2A%}E{GV)*^uXnE128@iZF7P.6{hSUGswZCsU8jM4IhdsrXP3rKsHQ9wlZo?R>x4tg&6Iy.KPBS^1KF*z9g89G=1N.?^I6hZ(&TKAl)vo{!m&u$1Gz+Svbikl3bbqNCo#v0iCAkS6wy}E^R$)yU{}yGTH2i(7-b$?&r/O@^ny#W9sNmMavl<@8@PxtdAf]9?{wxZF4e(cv[cS!ave].qD:d5xJ5D&9]/^!x*=#}fU-bia+=-2huo[MrH0K259U/=?2ZcTwM}Oc>mOc-wn.1Dh@5&{mgVOt}=+a+2o)=bMq?fzUG%^M/8.LtjbL.vW15JkKJ>H{Q&q7U*7atojz=Ho/fS6DIgeu/oixF2i>s8WQ8*mWD(AW{Zg^V0[I^0&Fps4l{ac!]R2xCOXyWG4NK#0Q1-Ex7[PhRukgQ3jXX.vyn2o1Vap$q?FV#8{b(vj()IMte=^%HG?4C(gg@QLL9Gyv9&kc-z=W5gxac0A?-pjCChtpCv0{XjQ-M%S5P@:G+W)RilQm>yW{IrAnF3h7z87dYJ<9r+gR8{A^02l)+r8U*YZzVTnIf06[mI7&97^eB43S2{W+>Ejsf09Xh>T&b%P!G1AJx5QWUtBE-kqzmsJXOTEf88302)esfS>BP)!mvF{x1<+N*Z:Jv%fN@1qdnLp+A3BSNLVxRakz+GIzVQh3-Ed%MHct[Q)kFktab1zg{f(]Y0$WSF0jVa2%n7):OIW]t5f8nK}j&VM<8f585Yd>eP+tpeB7Tfy0DV:UQ#lZw?5Wg%.[8cC!]%.Itlu}S2*kvoL]Oo0YR@[4jF%GOpBX#.3v+$Oq(T=31A^6k+}OY=1X!F&=838%/X{K4/W?T?%5}t=TbO]$j=>ZG3kDG2c(LO^qe})foBprzO#tdpj^*x@6K3SZr]RU4ycG&+Q)O%pn$Z(6>c/[*WM@6fPRezjEQUyb)$P6w6Lyl9tjTPLwQEVwXP9W8BXc$$:I}f3]Enr*AEgEr3vEw@N^IyLiDE<{l{&g&30h>{fqmyOn!Xs?dl#]nXX.*Ll0$RVaZ<3Ip[7%5NN(:SnuG1:Fe5B(KqCZ(c5)Aq!wk}5}M0n/DW[{<}4ga1OgBn17BF!OfPd(Rs^DMRG4E0mv?-ZZAtD5(vAQcMPT&.lM6vT[(v6j>F1cC{BX0O-eb[O@^Mjde<3g6$9t?KI7Rn>rk1#*fH]dMe0Sxs6gNwN[WI{A64)Bl>w6qr-zaAhFxa#xXeVtfKa5WtQDxQs[.4UaK$-y1(Kv0LIICO#)C*LWcASwEtJQ%?r.zumo!:M@W]k{^h?8OcCQaWHs6r?g!DK3s.&!f^45x73NZePV<>qlT]>0ti](qHo6QUUaq!Se6##arXz>aOM$#huhg8KwfX3zZ=>sIMJ8B538EkrYPZ}z$qE<E=M4wPRD54M@&>Os>-0QIau@AMaj%4kf<:?!+C)jCm:%{e58N>Aba!fgR$FmZF38De:%:wH1=tD!yA^/:Vf0/M*XNA1$qfc[G]dT31ROLR@EwX/uCO{7766iI2TZycTc!Dqvufz34?FCJq6%SmHK^*9WP5Teke>FfUKJB0Yp3r)Fd^bbe6AP+yCENu}0^jQw<6RX[{9!5KezqG$o1S?I

Bx(nKj/A$6-2*xijiQGzkNawpMHef9v3+L>h):V8]{l52NFp5>crCEq*#9[6@cCNzB:8/d+}dNL8ctLOjubS/-x>P8XM0AyAETBi+9wLTXZ?zjWA>h1/[>s^{+5IN2@n?ul?[S+$))qcY]A(-yspto?.Q3[ZyZ05D{z4{./u>bVVlO.7U$vXs$uQGa&tevZg!(ZLwrxrRFwqCG}22fN?M9dd.NllE}Wq+1&=7+!BFGe*21!It>gL-a>!wZG@OS]$^dku)W2FFOU9TtDrf7KBDu<7fi[Y3BTl].Iy:@S0MZ5QjaulAW4wb@et{0A.I:0dpyu6$FOL)nSIYuJS?N8Z4>&iBhiWv#L3d7@d9sEDkOWmv]l7s.f.Bc]}(qBD$wOiUu{C8-S)+B7e/=Y<5{gnWmzK0Yg!DKts&]x>g@&/u)g*t5vZIDfy7]sdFg66rI9s!LZ&P=!.v!:LA4^kY.crqQecu=^t^Sxb!X&S6?w[aP{DD.ZsJ@TAY(D}w/J<}bhl}2Lj.P>g+BNEXM4Y0U.f]3!-{jYEiLB9DRA%nz.?3OCT5[!n.-v(!wCdpfD:47MP3<&z$eaci^%BksXkh7^&ZPJQbM])%FF68)&w^3zn%y4}}Z*0s1QgW-/0GOvuuCeW./1aQlGLr/0ELYNcfjlhZxz1RDBN3#t4wW9@rT4av^>d*)U].>#]PW2Y+0T[w3]+y[+UT#Br2{@n0eIASY5nt8h537##c!6API6b.XOCIl)IQ(VXbYa8+!Ga{{lWqidHxvp6JbhulgMa[U.u8suChobyA3z@<5i6n{hL/VPv/.xxP%(o=ydb[I*Z+&879N%:QIIXzQALZA3W%#dr$$./LDj&/sHt)t:n?zrNhPTFPmI7#0hp4ge]5V4CZa9Lnovq>]OXRF8+xo!yuaUm]+xr+a]Zh-:]}roVt.[Ee?.HBvJu1AI(szZ]i)GVPIsrac7!}1T*X5n:5q(-1FhUvm]5Rh!1OXrcXF5Pm1.WI-*GgLIHbNszAD4RNWavKs:0l7uTHg2=9NRdjL4Nzcb4d%n=g?8V(V&))sD8Dx)Jl)>V^ui[@J+*JmV+LkTz]DE7M]Q5-Nolt2!sJV.:7(8exd)>RgpwZ*sr%mK+QnIHX6Vrz}(X1Dg1-23?vXq<}fl-LM7NeHI+&9utjzAUyAmZs*ZelWzNoTz-@OhL2eO=aqqK^Tp{Os>oyPM=g(u)Sp:#pF?2smPaI+xYxo-2>19PE>3(+4)p{Lsm5^?JWU^p*)$16oMVuty%j!}7J:s?mB3.tPzTE.n1hf$?fD]M[e3tWdWZYl?T4g^%8?8lf[X45q<4b]BZ#X2F2sTlB#dGz00>5ZU[:W7fJKd@]Tq81gSci+R(RJcs-JG=h[GoW}Ju4-D#g}e!/.NersnJRV5(vcXa:D%Ed6Kr?gaJ&=b]g*jF48nfuR>tybU^QfAM&v%w2=+[ovV#[d4>s839>ynE4x?2D5&%8lRX[(l:!AxWnGtSBcE:8fbUK](Bt.)U3:sU*H![G!A%KIbWE.fD?]H759wS%It)}ky@SW6O+{u(ES)/WvBXYcOo$T1M}adHDKstPG(ayLKh)BtB{B%()]7oW.5hWbQ^kkuS3m-m^$8o:1jT*ZlNc*#quxBW]/%}3ImOViTN<&RO:y+)L8cgzWJ*d+RbcOGUK.qHN7(5uJ5WfWeKz{!?v.Rsk*zb3B/[3b)EX&-R@GywfYuPmag:GE(o)84MTcFm.H94hbmUEug:HjxktmE0nal&g4cdfym-(*@!Pmnd0w>j+(LU}kWo+=O^!Uu$3!./sLANh}jK)#ytk:({KGXJ}AqJ0FDITZ/qdOHM&%/d-sbYKE{$Rcuv5u]%focB:0QIVfA?PJ<83b@dZjSe%:2QxG({lzz$xaBPUFn]Y@X26Flj^zMu1/hvU]KWaYmA&Y%e.rG+Qg3%-/1MqB/royFKbnny*^2m5E}.NM]mX7C?(hdgvkMrKBq6X>#Fmw-TnTL}aNKP!eOnBu$4bB6gZhn4-oQnXI+FXzh/c@Xx/m*HV@ND5/pVa5FBz2Y3pIWZGU3&iUuk!7Y>N5I?*l0zV2F/T$@R/Fd(!pwdSvm[UuoG7CtZ6O^OO!cezKGa2f)bnA4^k[G1HfqAp$EzEaO2TofEIjme0>b>Y]HzlE&9X*h(ywJV%/6BSkGl8fT?zVtb{P#OZ0Tk91fzsAJip=59:M?of#]iTjw-!G-F&hNGU^5BLSN//40w!X-#DqBDy*I)XP]YRAXa-dzmq5T%wprHl(J5j8q9X-&A*dja#Z@j1Q-!-W&#g5w6/LGEHG6+8fy]dFKXGQJ2?=6sNK7h{{3kI4/9vNu^W3+c]^rTl>gcP(w44Qxc!uuZH+hRfuzOUoN0N4w([$!HEy}}H7@!Hx:QaJ]:^q!2gO!zbY1-1.Te(^#3]bWi3.Vb5eV0/k/^{r>rg0bc4gt($7VUZf!7XMS3VJL#M8fdy6Xn:IqWYQA[x3t2(/$ov<}whSZjnEGq&!!wmRxv6Bw@qiRJ2R8mCXxXR=Pqma=bG!IKIxGBD<8/^a?=9uxG8zB990$:rmruw]6[7*:h:h&U:S.br5=5&#]Z8-o>fO4S{MxddM.Rk=e2oL}OUYayJfUrc({PlD}*S-&O:w[(Ct17sdU.7+q=(Z-8S4g$:Q$YA7FqoS%(Q6/)thlfRD{gJuj0br9t=OAhGpt-cCzLEh%bo&ZDO0jpoGTr*e*(@.-I^0umq&wPR0NX38@BW*gp6>Y8LkzSgBYs0:WJ@Wix*.$M&I!DK]LFb+ijt]EJ9:&.Rt5A1UBR*2@PorT%e}#O/ww^?}uPS{UD^i-AZ(1Z0J06OEWeNtRBiuUoJPHduHfcoOqU(K*@F[J%obrP1&@wHl{mNL13JU$oyTNg56HML]tp/aP&aG+Q]ce[0@R#D&(^z{2A#-[AQ9sE4ATkt^=jj]WeLD:y?*EC7*x*Ulx*5>*#SQ!bfO)If1XcX]55FkpR?hG&4J1rI7!ypgCCMFu>YUu?xI^k9<[Y+?DSXr6XyN7IzCKsi/E^OIYXC2Xez2njv!GXA0.-N0dE@tW1*h-oqh/XlvTZ6s{g#KSa]rv8&uk)t2p{Rx5)IR8kOElJZ%{s/PBtR+e:>0f6TpNUmI*zJsAwJ305%Xx%%A4Zboa}Y#g&2P4?.Q(I4hjzT^50hTLV{^6+y&@YW8Y}>vDL7n!A(VA{rgA9W4DTm9FrcQM.}E-OTKV9kG5>x$#?n?n&4qKhv}X/]s1]nd?r[HA1Vf5Z0*xd>=gb5l96B9[hLs<3d+}IY>Nk:>@m^1h9t:ZYYHRArJQ9AvthmWRD$4KNE)?RY3$vWb7Qz0(G+MQ*HggK7hJ$l/Ct4[=6m8Usuh4Wqsz]c/[h??cINLMIQD}%oIZe57Q4[G6DPKng0ae#b}&YLCe7HMUXeZO0gl2(?DpiCEqmpVBq!fiWCSq3XsEK-IM:>zy4^@z@ZgJK6V5=L217Kp:F&&Qhk@uK3O2lQj?jgsB*g#8>v@0)tBqgHq?19zJHZO)]&N(EFTj3R>teEgV:n1S6#PxTnZXnZ{oI)3l>@5C3Y{VM0q+0ezPUt-9Of/y>HK-GSBq%-g.}Dz5^fs!h)r5dR^x]O3Dv>o??8(>S(t-=Aj[jvXuXrfUp&BS=A=2fTAW?QhAnaC/er?[8:qr&V2ux{R%WI-(tHbxsVTdVrB$w*(w1(6rEG/.Rf@C?X%qd878Z&z)2=#@7&Ub=?Btk+COv?5fhxzrh[TxI[n6S>dR$z%8T+3zPxEP:131Z!t1NvMa?rA/B-pWTKr5=7wO@>bQGxp9AS^QP0}=7eJ.GGtg!T9KY-JyK?i8ruAO0Jmwp1tF8Lz-[qIZM/MOe%K{v}jbrb5znCEi5N(CJtEMR5J@G269D$hO}f29>#PWlxqcEumry[T3uRhm8l%-5:dfuz>uHy5}-@$EoJ@9lxMwjunF}:WK0foNSg0Uw<911&ON6=:NU8ud5PzEd{FbW7L*F+l%2:6?6{AGoD$+1P]SYFeBc]++&1hXGU+50yz$4kqUWgIQf?1peKDLcZCLi&2dzfEiB]V:v.j$t^9QIjGKByNNq9mU0vDX(LP&({-$(s#@-OlL(U-}y7bdmQvyigYJ&]]mi+*A[qTKZc)cEU=5U}jbdv2CF^mq9Glc<[U<>iKO$Df4O%ifp!3K?Ob(Fl6[fzzmvNpV0ip5)Y?1OhJ-+hU(EB!v?lmVoD(dQ6B5)7(&}AGGTEM}+lc9Ju{DOYAh}igjB%$eX:{.y}x/@9!k]IfYtjmE3k4wZVDz8SCiBKu-/U#W+i}l(*D6b$Eu^YP7&%l%I!}m9b4?aEiFfM9^)m?EdTV[1<:EaF=63j(7]98{?DQpKFJ0tsTS9z?Bd>7mXGMHi]f>}LxqIr.qp3:39L:skt6ADN>i!@bHm%gW:81KvsupX7/rcm*(FH%K*39HPjl61BWwWAQN1PHsNcZpJ+/$W)YnpM])%Kv)+>tOXIh?6a!WjPqn#6/)q)4h[nTFgqa{.FBr[=j4mgoV)-KE#Li[po6GbUp{{zKw.u)]}IroBHUh0-R$6*7s9AewB@2FgK+BdG=hU{UmpH>U-&[pp9%qZ$IJUP?ECt$TvR[)d(JHOjtgp+ZZq-E3v%!q%L^@*-1tD)kDbR>Lp$6xw9z(mrXcrMv6KSvZMIve1qN&.xuUW)[R53C2o:K>opfwz&n@*f={otlFbgPSLM!?VGZNwFhjeHLqz4dM/2+j/m.pbB0%c}1*H=+RlFKb@Z6iVyXiFpS.ZR6HdU&34u)z%474O(rpkyW>hUvhM?>hG!(#dhWya&B+L?u=XYoD=tl-O[jC(!I3Zh5f>Ei1*jc^ux:aU[na6LzV-eNK1+mV&$oAnZxr=Om$oEsM#myJY^V5(2>zRc7@pf9*iA/NMUSn5t<+2)5Bd?%vd&d>6A+[a2gSa::wG0?QTHGCwRG104T7K}f0UImsDp8n6NC.WfCc4*k0RC.^qwoVe8RnVd:Cyb@IA7K*37z:kjVf<*fwWZ9E=o@gWnHQpfrM>hDqlf-?9dcWXp.0Du:nG5g@IWKjvx[eRSJ0nBKEwH.h3I!k)s+6&ot}Xg)0N70d7#R]xyav4Kvl4^Y}!36i)7@r1?}cV?mmaOMzw?g:sup]H$JaP:xV$SIy=kR)<+1!qc0g.uvTn=nC>GrhPI3^5LV^j2%R6ELon+Y&ITlR@tnpT3?*QFV[SlfLfK$k@4e1GplADs1!zBkx&@L.8c}8gEew<^R}u6U^!Y9H(vi&]VLjWATR)Q*0/$tL}e1b#Jujadmx1&(-tX[Fik[UP:U8XhBR{e#ACS{ClP{*J!yaipSf?l}lm%RqnkfH5e6k7rtWm-p4AvMZ+L0iyp3/p?cAW{ZJKjzgyK@..lpzCG-RNPof0YD.w$L=]c.F+QgoS8M/$qk]yQAdR-lyS4JH!c/pVbtRXE&/JAg#ujZEuZ*zLiR!:p#PjdmCCtuFhn/rPlGGcousM3eP%A-H9)mp9..kZ8?@@fQ0flaQWrUqb=+i3!23rKw-9CaRe^sx$]-f34:V44[cS@BtgostGO2?U#8e9e{Ig387xE-#t^WuR2YX[)ddxrQnu$9eX87I*-kHe52he$+6p98xbNxICp.yZEk0CG#QroeSV]?YWlf*/U8OEkRHTZ@mn^XSClsYkk/6=PhL4ybqfFb)2Ur=H(P?zW/K[XQGL3d{@hxS}5tbj7p#gfq(#p3:!3si1+{-6L}3cxlmok3z^r5mw=}nxP1^gESvtazK{CYvy#hB:WoX3GtwjgQH{UdGFk/0gwk*5![w86n-V?y0]#d=SG+=RCY%keKz$]-#=0Rp-5)zIs)uBRZnJsLZU@8VIA[K]zwjzP%fs5RwQg@Ti!hLrc8Gdivzdh+L@nrv7v:E+&1Net9&tY7-aISkhYyQ-#f@f6O>7*L8-0f2NOFD$hNy3xoV?8-3La*7b4<*r&NpgF<&g1.@cLuRV)=}UqULwT0D5o(c1e>![o{OY(r?#7+c0OOf!(D3b>8GRg6SRJrwq97IZhVMGg#v9uG:M9*yGtY+rPKx{mJz!&pOC]CrXWTQGQ&4]fLmzQy(=Q>y$aDlvqd?jeMG/Q9EUPAtXd1z/*fNm*M(D=(Nn8Fr=jbt}@eqMitwV7[qJMa0P*y.PT.C:*MKZY96gSKJ+t^*h$uXyGi>klH?QXpByphht{9fV[{3W>=JNgpsw3j2dKycKfU7k@hVm=vO!Lfwh&k<2LKd/b$:}]%)vU93T[F4On[)6YM9GgJ9@b3Eo=o4ZNAa6NNukjj^s/4c@UOr=PfwaU7f/cr{*C.6:(=dno6k%.p8Kv:C4PI$?@KqmcW+KNf!hAPf^:S[QKXNYal08V&A)2(#G8C*DwiT#o(C2U0JftqJ=H*ms!N7y<3hfkCO#!*uJgB#yMOA8n+dw#L5Noiw=3%Ldz.+lr+C87&q0/#bi/:e[%N0F:tQ2OPZ6I15E)VZ*f)1*O#FMzRJMC^zB^5QKvt[]PBDcV:?b/YJ)mYy>Kd7ndZ-M:8k)8&zvJ:THLJzuqP]#4f4RjJ.mlnkhC2<6Ec>A2g3(ighjaI(H7s95f)TyIt::VAt%v-jAhR@L}wDQCr17!N8spclJ9vYy3bS#$pN6@x!J25sLd=9+T2k4h=&Tz3MX=jMM!)$?1*[+tucxmWT}E{59$Ut>!mGDaY]()4]>=j0ljQ]MH3Lfci6C$zo5(v$FMc6.IK0NJtD^Z.l-IPO]:^5iSVoBCDhW:^PFE.VkBC=68P?*0?^n?hd+hG21zNbS/Vo^+NfF:8!V&Q9P:o1gSI=snCprzCO+OS#A^Qbjw8Nm}GCSWxjn0or}u9YoG)L+[}V+0e/i!lgi3VXrQN8&g(QQis0H<+=3K]Ur(mMniBH>Vjl-}CzBMSUgQ[.rlvv?gwh:vkddxl{[3nUj9+SR*802%)mA6!mt>pF=b1rV{.Qi4@GV[PPuev{U:WT/0-3*UT1xHBUO?uigLjFPYy5+xcB(]:r{X0qDFyUlzTKksB-WzT5u8I#Nfx8Egp!AMob/augPTqu-AE*yo>=*Zn$^D92^#Y=DoQfc!T+Eg5#Ndx:0UvA7vEf0-[WHK+>+2JknY^{0MTOb<*5iz:orLcb%ARBmkTaNH}DCIuAmaOG]F*a0fh6-(CM}5:B)DH&0SiI5VMP/zVn*OW$NJ#d!z)]!CijDhKW]Cl8jg@=v]{q3xuO15d8qs+HC(&SYXG!smUGyuO:9C4x9]>c6Uxj/xR6?Lm=r[lAMCjSs%kaPW[yL0>fyP5>pPm:KcVuV$J)8ZmOxOY[jAI{tQ7M)Gr)G}.ZcD:.s<1z%eY0zyuLH}wZ$rw})(9aTFLC{X.vP1l9zEEvUf4k4vZgl#Y)G0^INt3CBq=5=@P{9!*+}uSx=:S+wmvW[vTV=<^qn1(wRb+vK42i<9/F6ssgyG(0i?srWEUjITX*8HA]Mla2qtPNoXk6{&HlPA}T%DvghacSOA]rC]CIm)B>gn&o:bK4:djJ1m1(Mn.$P@9A)yKqh[ewZ?2$-TW!H!Qr)hI=SD0#G=vB+bH}ma]@6LGoLWi]3}pT:mO7?v{EqE8J3@SmHsq0LwMZwMAL]U7yQ{Ww-ruVJxAsIAwhdaVosp:1gbGe]cWC#+fSr=!w$*TXzdVnW4XnzRxU0mGxe4JE>(xtZX4Co=71MRi?]tjjxatSXdWCTtfVSm6a%g}zs.InL19E8kQTlJm@>tk21]v>03nt+fnvy:isz+?^^#.8x3*crjQ02tf(+Eh[25*zgX0/UT-$1:))U:XTX52]^Ig>LJh4fVw!I{qknN[g?@%m.5xJ@yicFJuf.8WX5?vEAT:$r5K>U@/gbEyd&V3ROH![+XYv^N&r0!u@<&+!qve/jNh-bW>21^yyroHH9+}9oe+oLntjL=*MrJ&S+zYS}tUy%rvyw.w}Lg/Vq$CrZ*CaD+-b#tZt5q/#T3BJdZSB9/C*%]Em5(W+3A8mSliclwv97}vbnZW)/p/bgR&):iwHBVOG0su:)C?M=sDoDkwuUT8z(I/.dLv{4oQIVW=KV#uX>@V}/SfppNCy$+[557FGiv?y9$<[aqpFaiL)z?@JyIA*c][GGl2XDW*8N=<9fiFr>DbjZw)lXj^tgfYdc$s&]B*9p]/dnCSpCxLi^@wU^=7dKD?Y9)F$9]3Y?E.9Zs6Y^3^99RSVn>z)ujdl>#6T9EP#WTRoQbdE+$p+T]sU{([kO)>-rwHK)688PEQPf*tLaY9-0Fd=]]2-W^t5u!-h95Y^:}tkRv^kONIc}qW:^)]pytC?{+>R=7884Hz9cx6gq[lAo5GWt3Py.*bX4/l-%lu9ZA=sOfK}d*JMW9)hbw^y@xc9X)4j#OTeQIbk.xOF}A$/.G*]65n/K<[qlQ()EC&fg*%PrBV>B.+KrZ]/4Dx3LLUhIXpi8nr4-9?3Kg1fe!rR?Bzm($s{Wv[#g}cXZrZ#J(iK$%uXRtZJ8J?4OWQbD:*[:!OJ&jx>j)jXN:{VUbg-J:W3ldDPdw7Up3.mlQci{v0}y%>GQ6q<-kpbob?l+AnT!pV}620n@>NEf+ekhoKs?g?nR/iOrxo!u1mrAIsB$)KcCE.KG:4%RW+&:sS9?W:w2nnfpA.&H]XPRb5Q0-8CmQ)zWE!Gjf}dzfUNi5[%E-sLyIpSxXd&3AHqDhHYUK0-dX}}BT9yd/2SU7NMU#W64a$e=rC^ewsIc1baXV{GxQPar?&pILnEyyAgQrtSGR8cJ5huS9@:CKfX:vxsIAA0-4qbw#M:[M}T0VO}o-!=+o*p-N5+EIH2.3.Y1w}Wxqbu%:q@*lltUBhyWFaBa4x-iJ#v(JgLcsUZ!F@Gm.)8Nf3fG]pzE7k0f)6hBDb=ZY!b>wy(aN7Yk)vf9XJs-w1:Gk0FArY*IKG0><:ARU@?%NL[EWwtC!B49GE@N.iaKuaDGXq(Br%6qXa{nk@tW3gvSYd)RS]4h=7MOpO9^5263Zkz:v7b}}b@u:${z]M9j*=^l-!En[ba-6{NbC$tIFmk))Gb[Eyj{(YUn{(}MPHax59:-9HY&v[M=2H[Y349-rqtimrG3*[-Ik3Y/i0hk}aF?h#P-Fpz@1PS$*JwRk4#szGPn)cGK])8[Z3&:(x@^=wh^M>M--L@]-u)PBrobeXe@{0X<[7%QRVO2%S}-}Ca&.ZClo$wMVgc^7!U[B4l{h#0a(6c[wfuaaxL9RchFua?nl568qs7hRXau>XzQQMx?xQSf%dopODCS(I.IKA0BE.0RY)k=anxaC0V$izy8C*6)^tNWolR+*pA[dla(k#*]PgX=/DzM77ClkDTzn!Ru(>We65?vRDONKYB?x8-[HnCJ8oJ4dCzVdeY(aAbB]wx:XTnMUn5m&y}3V0R0#mgQ:-r$Q>/jUt]+EzV=WjuxgTnimKEON8TSy5&T?2!gy^qX>W^gW>ARqw:}?/5%X4W)PDzjV<(6+fE)-Ni458N*dT!](ZmS^$^*2=M<+hsp:mT-xkRcdrY}H!FZ/Wg&vxvE@E)xHJ.c./}(9bR/G9k4BoT(>?+RBT#ugom=[PPX![M2O}C?[dR4^mhVzzAw58/?t[AGb@oNbVC]D}xz$zdKiij0J)eZmi<=9VBV!CVAy9uAd&31+nxn([F8Q0C2W0Pk{5+NgqkM{Gh@NUCm{I2u+[OB[i7Ck5R^YKGyOMrOw:rC/:ryM(8+VyzDiTYo.Vt1Ev^[0mHLH5#h1B-jc6PwS7?}TYWx/%#eNtC%{HNH-K(!.IOC1f0SNUa:6qt&Tu@vnEX^[XfO=bFm/$@}0S}Q=R0s&S+)19[-p.W2-}WeeJ%Wz4XbEyaXSxTaofd>(i=M5$I[G8f3K>8d^)orrmHtgkJxN5*3A2?R{2KDK[9)BXv%5s1:a(O3%YDKDmaJMThm(h88L!0wSX(2Vpqmjkkj+B2IjHM6.nLn=2%x?xjRnjHebsYt4ZwZ:{7.dGp-@LJN!1VM9-G[+}>@ekO#Z4d(@ET6uvo!y$d[ANVTL@]l1-!Ix$Ffby#cectRiLekb(IK=5mOGmg/M7f]E2yCRa+iA#2^#s9WrnIPcLb*8)C3E(/S]WH2SHXBQm=ZmQ@[PSEZaTlHMMETJwHUm6g}AS4t&?i9piDy%y+)LklWRZcCg*[Qth?h))n(O.21=6#Sdu@4+)HPb>p-B=pYFK7t.$SI?Z@[OI7Ke-/rq.PC&Qvfx({[9!0MndKnMwV0cE9s=noxO^N0FLsH/^by?P0rT3.Jbxf}ZAB8z2y^d^Bb!P$Q5M}zl-mvaSqPt>dh^Svgl4yj@tR&w6WXsAE^-wj:WeX@+ZcdgVoo:BL/5$Dakmy:ByF)EF.Y5AUB]:4G-<@N-C]#(4J7#j0aHfCRWy=k5I6a2N8wmF2Ffu%3BBEE^MI)LE{eS&])yjax+yhEW${ix@Qm1TMy>hXKRcdv!9F>J<>Viwm)ZgzO3Z}/=]RR3fiWlOOD!.nl>yUua]&Ea<#n#(bBT.PyXyo6v%s.Q[Ix&uOB7kOGP1U+{rr/-uLetPvVbzzc/]G)?n^Vy{jiyzBp.Ke$}p4ISsnu1RI>&s^FF*F6!8(W?@TBiUjyS6.xi4Rh{zVNV5}CXTsAzWfI{nql9Q7S>rBkY!$4)$4w4?9*2zG-hN9@H2F]9k0=olBwL{Q?CSpuIcdS1i-HHD%z@M-O+?.e4xkf4oTVDo%+(xHL#wiR/7Et/oANMBWYHQP#kJkb(m54xOSb{{IKT5651{$xIwM{:GFPX1{EJeL6mkAH-=KCMPff}&a2qv/Q+W-l(NU4u-Ty2sEQPARq3n-n)>dR!/8uGsCVD4r1K7E&VniFVSm^Qyh.6oB1V7/!7zzbHiY&I7fLg)MCj!w]rOUPRHFkuQ%to]jj^(gtjMrZFzgtL7x*Om:RHVJFj-e:)#o1]fLLDp^73NBulH?jh)/2wNqaPXXVlYuI)V*r9$t*C{K6!hztK.2?2j1*fJbnpp?H>f}T5iHu871Tv]i^DlY4)A&VdBSsl-4H#H4TGvq}:^f>=98wT6>u^lxGwg0.>L<.87{)$%z=*^HZbt1m?GB)n-v#ZkuHxFu5(Z!L?>0jC!}@i.%[L/8E&z0T28)zztX3VAy}Sa#rRK)PQyx%ULF8uP1AD]p0iaknBhEvVofD7(bqA]NHT^e)x8Ve3rYC*%K&lMO[d(BUp4%!d&]Lax!Q8$o4iZAH99O+1ic@#nfA-SVI)BDXG2l.Rw5aIDtNhLQpWaDh{oLZRXKQT}Wf6#6L.rQf$l<(bBkgk!t=i59F.R]Moxu>L@3hf]?!55>zz@*r*DPZu]xzD/rfd#9%z]XgjPc@&L4^/jsVgODhw^&p{Um2xiT5z9f{Dr>gT):M>WU?wzEZae:pP)^k!GsF4PHvUu?XjqIAil>}D3cE$:sJTvpw!<69J3c=yfJ0mHq7o*f#ztuFkH8no6quy+S(70]/FdHys3GX.=[341V?1g5o[o&zdKm!G$kOhy2bZI*}H%yBowt96cQ^40FwnEZ/aevFV5e/[w2VBU7Nkw(#v!GV1O++j}J8ZI]izzg6>$E21d)J7e@+/7>Aoff%4YbCy:wubEAm}3hA(w)T%7!{&Z+zwvXh#Yp]0S0rObHd}Oa&k-0sZ&Aw7jK>+o*5qRmy3!#lc=9BNgWB6LGS=pv^r{nml>vCNw{IQK[5-duMy++$(FTUH{m:4-YwMy?#4rmxC}=:fGW6(=F@dX(uDZJ:l}-+VM4P)ejyZ8}[!jy[6zcC5Nqo5Lgj?RcGqy^Sh52Yb5+%F.?m=:gAKsV.i5l0-n^xUS(]s#Y*^eapG3NnS>#zGLpkNUG>h:jXr--J]12pI8ipzg[..)37(z5#*ahIh*(rxg0fGOWNJ5LrafOHTK{Nv-TSWnsfN=U]g03sZOcm9}47cZ#(0Ftp{U?}TSileoS+Lo^dA5eV@m)*7+#$B?0xca[bds$PMa7Q2wTz<-VlyC!okOelC]ZKP3wMRU2ed0ATE.fl^Y40{I(MyROb>/D15C3pZgav+QuO9)LC::-61tb1]EqkF8(TZElW8Vwt2N291tBxU=7.U&?#=rcI/KV3KdY}[7FOQa&>-g!6//0PtC>VbD<6IJY3$#R5elj=PaCcsxE4mKgxdTc1soc!Ztg1n22X/11uDh+Q+z]%jK%7BSKsYxzUiqjrmap?P:p3aU}@t4MOl])fhHTx-#Ji&>+9G1lQRAmH)uh^{?IP%rR>.Q@v@emt#itIZLR/QV@$#Mo*/x/75}:R6=oB{3uH!wcX*s.z[{{rEN*oR)+x?d&23oWcr4Z:}kDN{lmnMUe>Dq^ks!j8!$E=)X:RER5ro0T+5TtIzo848v!nqM!Ke{:#g5^PKLFH(vq?U0-x/ap.a@jJc1KFpeybGES/52d.a=zo015664CD[/P!%f@N7!]>9dmHG3G6%R%NJD4I[D6!3d8{tl0anq8.FF70KL6@W%Bn]#rpk]BTTw=RH!jSemYo5X.l}@mX=f.VdALZNTZ=&OdcI>m298y4{7jhvwfRQ*Wv}e%pToF2J5e%d!(U!ZsbSzK>c#.gAl+c4*Ma/M&>Xi:y2N*xWV-EZ/9q@hLVa8&syuXCCW12)k^O*^{37oc%X(Wu6@?etwGAxRVn.Ht8{TQAY/X61vp6+i*{vM{KD@f>mV!rR/]L*#uFI(Lv(<6BSJg/uQS.Anpqmh]z$6uDpt932J7%R!xmUZyv)r]W@Mu}h+s5&FdxvfeW}M0YG]kZWHoIBfXKk&HNn$16w(dJf&9Z/RCmh%]L8pH-I%m%9J2.-rf7rp9!S&vL(y6:IW6V%yzolcBrAaWKQ)9%Z@J>p=OYX{TQd&:}5!Hq784OZ)$]EG[)YAcIWP{#+zkla%OnI{/N?]3*N:6f@sIf{oR[Ls6qDUA^OMf[&Pvnr9o>xbRYF-96zikxQeepJ.:87HtI:8aB{8eUCPhgdyCjzkf=Ed!Wy3nVHvy?Wr+:Vf?GYj[lgoEJ7S]nEw5H?#zM:XOy<9^%/0KXjt>j7G3znabh8keljADLV1P+T7a6U]={u6fC7f$ewj/UHy5vwk8Nmg2$10@bQ73s!hbu7^zd&le[Q:0=lT(DUjXxaFmvGfI7jVAIdLq8Q%V%4b*9I%F4y+cLdmV6S8?29YNIJudzI-LA%}Usxvr[@PLT#it%lWt$-tRx{&h8+aJEvxaOlplg2I^iZbVXtC#=321/DUBnljwVKy&Q(y93I[Efyl?QLh8Z-EDA-6D9@uU-yc#mg*}1O^sg1[hqTthRGx}I.h01&*]sCpVTagc1k*3(MVCX76]/ZXVgB%cYdz9aimU{Vwig}Ug!+x.x/gsTlYFnqqGv7eI@5kEiq&-:Gamc^j3]OK=d[%jQln7kha9cB:)BPDOor{A]=du.<)^qfrhmo?BJnn7xIm>0wU37DbQr4tq.hQKLx=4K1/3xmv4lSEpNiF7GO7Xuv[pdME{Mib5%gf2Z30rxf(u=..lb35(U6NCLBTWmei5O=]!vNStEAiPKI-o>RO.vEk^[Y9]!u>CwWG>87dx}rLIfUS7$G9Rq$CptW51tq@92R0uL?7[-.Ux%CzX@X+.gAK$tG3nrg3!n4GiEaAxM>$}F}Ak*/7l3P?@338t6Qf9zZ5oY%=NAd!aMTa-u>p84GQjWPo=0g%FU-}{6eG-6]^xjW6g79@58OCi:fGIUGe^k>NK)/{YI@7<7%5)5P3&}Hw+i)HDIGMi:Z.:p/!i%gZBD^]EV{x:nb!>p88w!tx?q&Q+>c1zim1yQ.XpF!bC}ipw1Z^XPi]0mfJx*EsV>op*22xL[1G%ws]5a%8wn-SHu*y%D+.FodT]G5n$UR%pC4Z09W1Jfge9a1Ncpd-z!JYD=wZ9ZoO2g5*5b75bGWs1MKZ#wQR0S=qNEw%mU}U:ZB3@FyNn&w^+nqRm*s3V:yMXHy:tcEOWvX:XUTOX66WLd@Zg5@?SghU6du*Hys/w}[g2moJSk->sf7ve4lLI%IMxP/AHM#Gq58>u5Xr+-xGR085exH71?P5KHE&%pc0C985Ppzp4:F[Fabw>J1OjdB?%eqt&]-TE82y5MKm%mY(Jx2h<&Kw{u20+f6*f5%s^k{-y&e27x$evv7a.)zUEQ-QtKy&vyJ=w7sHVDjjC:TKnD1KQk[WdvmhUemuY#IRbo6XN7{i%2)#(ay#zDeQj$gZ>zcoAi*5KhA-R6A]X@:FEQ)P5*%30IiDHpO+136OOJm%WP/RX-w.2TpaUU?D!IF4xnstCy1x![T$w9<}mCx(}AL{7uh861Q7-c]64spBFIb-CwZK=pi4x@UFUi#??.eiVqhO.M1-&dphx6/{!jAo4UP+5%y&O{4.b:ZR[fI=!K.cyn&)J7?L9JDd-7Nv)A&i5PIm*wdZj8wb0s)yo.!D&oLy4Sdk)Q4}9MRLbe.F$*dL+e:pXlJV(:841D[f?6A%()J]>x7*TrRU@b:CD(XB2yFtpyYz9WEpd^LmE8mudlE0&iwv{lj2JpXu79&rsLodHN9DB4.0Fvb]NmK[3*jV4OptnQf(p/MqdCR0x@WrjFWmHR.>5!AChgq9Yauup.b87zU9+i5OxLd+l]9-pyl(@9V.w${oCS{S27TZ$NBl%<6%/w1ou%tLI4aS(*&WIDA00q9f7Z>}5VxK8IvSDyJn@}3XGNHqK>ygQ8Fn!7sB(chzcXJK{Uc=Z^XpEwWs@khY^N?.mmOsaBhH@Wuy[{9Gf@evM.#(fkpiDlO#[83WG!+8*}%Um=+6^WSGcP>Rz!+GM00O/?{A+4^sIQugv)<#i6F)W&NY8B*^[z>6t{YpDwfk{UH!-=cFpgK/<&WKYJC8PjLqOH+z@LNEcMx60yCJ[2G(#89^wuxe):^MS.G7=^dQ97ayjxs0XaI=$wcR:gRkfTbgE1Y$lOw^Xwg?p1iPSuXr&wPF/)(TnP85Po]-eVqFRYnKA*^S59Tv08NY0K0?-k6l?.8g?o=R2#fPd6:GdqxmQTD}(dH.u}YeyR8(?%3>btNrr9>3(Y5v:pZ)5THQe^&1[s)*ALnU]JD96emWXzKNeJ[q>9fO9UQ^n8:ei^^!$I1#>i}N@ZSj%2nE6b3svvk9$cWEq*PQ2kq]zUZ2pE0IKsVyBOG$H=v9FpmRKrov/3A^tMw[(U1?@!k=bBPAoN2mo^)$KufQ-f*fOyhB+f+YpzCt@qTCI%1vx%[l/%=2c{fq3smknYo?L)]^g>fiTJP=&/68bsQTTp7b]A48pB9Tio}A/8{ix+60(3dDROpT:-9n!PX>$BDRQ{ioevp2K&}ezy+W41-KYy$lj/]+2{9H6wZ-B0ex@1LWJg=/ciTgxiNk0s:/6G)[VtgA!}Wr1{-UiPqWoWcCa.i=I1+9.+bGrwI>D{MMm4^]IZ7$uZa=vH9JK9Sffp$X-%JwT&6C{/79dp^p?0D#Ib91j{z}Q[ALh/G/fj)GPQYR?gO2cn.JpkeGRIEah)aM}&VolC&p/jt2A51ZLeI0yE)poZAhkIB//[<#gIrfL!2eP]/5wzzZ76I!MytBpLgHfqBFgx+/ZXR!H%>yS30x]G2S2$6Tz9e/ng*^txKpOoS=AJvnX@W.!YPin>Wh#R+poJ!g$m8jf0mj)vUhL7^:wwr-UvZ$YRc@&Jb4vN/d>[K.9J@Tg8m#zMl>cf[K8PAVB]c*k.?P}L87SfX6$SzGcxnYXY+(%z6}hNjC1?B!7?kj%]$obV}&)MIWBGHWGOUWhyfmvy-9h5LYyf#6QuUaejPEKinu9h0x0Ci7(3(y:KD*0[1%k}}&v4o>LzSt}NjlcqzYQM7>OhVswPecDpQ?nf(*&5?A.T.>$yJTjQjBZ3zRUEnmy^C>k$(1B1aBWON#nP&#owC4qF8E7Vw/!FffBrTeZzAMBN&e5U?4mc2dZ!LHazm!bQ{QuJ(kZ%F>[dMEd2bc9oxS]QZ.>(L9d3EPAW:Og}&)qR3y:#XZ).>%45[OG>^BKcjz[$gH23A!ER2hUHAl#l*V>tN}%{hAv!AaAKdAfxZdJyQnU#wDy{LkELZa2L&WelWo]se7Fzt6RJb@zMc*x=w/@jKi{nT9d?zE#)He(aU]DB{.UljA8CC!vr?4gQgASBcwCAd3Q{bNjrucCnVAI#SwCl.]k=mg4WNXa@eR0(JV-o)IOt$$tYGYayGEc@y9D(KA<7sU+pxRPljUbp7x2ti@dVfyF9sHV&[BxVf{@:h:wq(82w+KH7o^D+C1lYu)>Jv*0J7lS%kzu3.&H&a7jyu!LTh+z]A(f[yZY/8?P>YQxR{N0#+ut&V*z!w>RbN1^)v[(2+)ZGDfzx]p{TUa^NA[R*1xfDc6Zy$Zl+6EKTFGk-2Nyrv9wJ&z^/*Sv${mCr}ArEzQs4dcrqqb0RlE7p=w#yRuzX0(Dr.3BvW3TM%-0(x^)(j>R5XS8uOGuvs:eQcJs@=K7z.zKL8u])h90rL)Cy]F%6R}1vLpq5ig4xqs)p3?IAD:uv=>nC2b.[U/VhN0wi4Bcbh5QCz2z=BEosiv+IMHL/og)Wh-oC!a]y{hF(V}/(CMmS0{{JT?mtOH4Yx/W9o}pnE^dn1Fy@m#f7XWic{{m*bY9d]l7%=T}lP^/)6qN@=JNQI#YRm#$FrLWu?5H]bm3?5b1MQnQ76l(g9<.S+I.gC>rBLT)hW8zBpsF@MY&zA)UN=yg93*WQ*ofgIc{ppu=SHWfpUjlDljW0fQB1Fds4zx=9kYk5pUCpdj!HZR[*SXfl(M/YJ[lJw:pD?xFK+>#S<[9981(>O9pcYE{[spkxLa!{@Bw+?S8fxDzoUAp{r=jHSue(pN}mZ9^*7qM{>Fmh)Sx{4bKIe?L}erc^SY7wD9W!a:5lARgxL?$lHM}iB$[^je?W4Ape6u.olvItg1{2=DzWvb^GU])JCz=Qi@>$qm[2]za-K=&DvdCfW(8r])U@:zP*osIzp]-R1{t*4-Xxt>/]$(ICh{)g+=m75DuWIT2yWFP{0GE4nU1da-js/1qW}9V$uHlpnm^I&%3TaaS)vF/5plxX#0HI&Te>S)n#Kgg=etEkTf>0Su7.Y3y$Krem$U4PticpEorUebo5R(Aw^WXBV2zi@K6hqM9#/NwA.?xhOq)ol=!Hd!lxjMm(pl.0.0#NBjj(1F^Acim/ZKXrO9+Vy{pwTOn1LKFq5ge]yGmdUR.8q.Z%TB5dP?z{=T*6?YId3(c2L3&0}3=h7}iNQq{BA5x$!az+VXE*?8}++U1U(F/6Hq{)E/O!I^3+p6[1]3J{D3F16}!)lD+jpKdhP(RtyXmANHL&{^(lhkOV]jWh&2t/5gw[w1iPki(sNnh[K?0?DD/F!*YleiK{Y#=:CZBQ[[/fE&q8HPMd[GqMbSf?iCMrc$kcxPWi=)Hl4l@k(1TSsc0NHNJ=7wkr}32q0A4&CZhj-J/Tn/2E:#C2U)LiHVY0UV[=A}]d9$}FX!Ur).Xkbt:wEsgMOM6Rj.yjk0&Q&LH^P=cYBXm)aC[?=9OP2l:DGMg#NB&RLe2cv7Zqd3T[Qv:OOQ47jB)}NF%<#&qR3t+a{SLYqux/cL4&qMM!/#!2*Bfy1HULslQPL%]irD2Bh%77l%uwV(r>[(ZS$U[vz@[617tl6Q6.K{xI0UEJw&TJ!os%C@j5@Wi$#3k(>MNkTo>AwSGg1[jS=^7+vXE@[+(}v9JkUXGk1]mg/(Ro]OW(pgUbqsvH(fLJ[PhWt93nh1X%]E0I%k89VvE-@H:dZE9!qhYI8]RCx--bUH{l)/r2dLha8t}17wiKULAf?KQJUU(%R2&6%mcrHr0.gAZA)sh*qDPui+O0I)sM=61(^4j*G0Mi97!U5cM34hj-nzhR3:p*MqMWE%E/f%%j)-v{.kc!H2%?j==hjMm-PG}atA9pCje2x>o]lCsGml/09EP8h&Rr1bDDaN%%7H#1zzNpX5pd}bcjE^r9JWrJuzD%XP/YF9Jz+O:XB/{^r+WgW[BRj?Pu>*eK4Eh6o?m8VU@Mt8M7OmZdS+wtBdq.C3FMExlU]YrBmLEvR[!!PD5zKFVbBubmQ85Z@0!n$t$PX4}Gy+B3ztK.(GjSHW5Fo>uV(s4@4=r}cE61c*}YI8nK*A9r&B!QixcypK%)[y.$r.YyQ5PC.3i=5Qi}tbyZcl5CK:hetlxQwX+wKYwWy*1j5?C{:)zdR9#FZ4e=^9?]VD?u1VpdvHn1N2XS?.dQFDlTH{ROGzZtuc:tZ?uBj=wjFgDdq8Bo7yx8Owszm)hFnut^hLoNy.l8N:W@}L00yQd@ixfA{kc.yaw/{Z<@0LW^wBgkP[S&5F1*K4y1Lc.IJaFfHEK?&PW(XgK}+{gPBs2su)}xo]r?htN1@^Z-{dp8}]P%m(Wy=/26sH2?@=2a{@%2Xz^eeVaEz!6QEf-+R)Lmu8s&l:>]11h-M[-4M]J!zc:y+O7#OCuq.&@[G(i**K$@GvI&2t6K!i[STEZGND7]gckQH%fDCV]LjjtnUKyQxlWWGx9p]Y4=>3KF45jxF[+fNZId}rLh:ido+FvBCfp%Mx:QDoSmDv:DHE60WD2.AG+CszhcvLU??QUWC4]r-#bx3SN.Lbapz3?H%T7Jp0mgCZqsBblXzkO?a):=U4xv6Hng36]s1jX5bK?w7HgxfxYEH.G8(W9W-(s=?za[JwGmX/*y99i^yzJea%N!)%uyID5]4p<1d*UGi7[Og^E-v?}Ks=<=^}76NZj2!wFL0IcZoNhtIM<$L1{y/bt?e]lT@hRUPjP/n}]AE3Akr%=%GNB>vr]!-ZA/6!o-s>23xmkO7UHdC:{3n37NIQv8Ds&vs^T3vpQyePOeW9.N9}[M-wx}-OM@]s5=Wa!?Zj0aWJUQ(y}Z6w4Ix44u^(e@oc3p8cQ]^BbD&2JX>6iGRf]D}%R5=bYr=nl=b9%+zqOonQHN@jLj=dd+e-?f:AC0.@&$Ct4)p0T@>PZ.vv-}qBIPzEzmvIE)Nq!Zk(o7ye1Z=+e{u}9FKtys!@qn[Iw%mVp-e33M@P5Ygw.X@J2(1-tq88hX18+oJ^8G<@BD>JMOp0hX>E0dx?xjX@@7%p8nOaw49?AITT3lNuowYzXCjU5H)U3Uiuxm*uW5w*83j54X}^q=0P:=n.uIdcWNS%I1O?]:x.5m?yFVhZuyNS[*sT->zD-dIzqq[]4*G.c<3tdICEyi!&kP]EZW@sCxuv7Lg8)d3kgK}if$z^Q7YbAxpZ?&$w?q#F]IzH7lwkxkwGip=KGY:H80O&L9xa4bQQTZ5w.i^kh=6urcK7kg9on0jFsLyV2O+%l(yj@tu8@eCQcLbB*Vj9ANQC39kn0R8N)(qx#b9sf^:wQO@O78h(Y*HvXIe2^Xl:d6D/:}H!ws:oM7dXyzadFVA.&InDHH^8FIZtBVDK.d&5:SbcG0o6y$tIrR)cn{Gty$6&5EVbH^7hz8q^lEeoqh:2dpP}I*vp-V#]Bpadj$VbrPR=v759ie@=]w}=utiRZEId^r}?(8hpD1lXXOdqM*G3IH-zZ2s6UU#1hnoPlInpt)hqLu2YkjZHSj0}WERXvnLNT$u]Hj9^e!R9ZJob(q]Z^.tLVD[/IpfI+RLTQ!zm*+N/#IjR(UiZzp[s.KhzPANg4$SsVX@Zwqj>j9IQZR&$SaIj0E9[rLE^/frOux5R8noU{KTCSJP/OO}a:028riJ)xn(dvlz!?hJ#D+$EcV{.hWxli%JJAK=?Oj9Q^^u>XDj@$eFB36ptgMpmTjGRg#L3Y6R)I$W@e]/gSEFXN!Lu^BRca7F3gQ#ST0VOKR.!{hz/nL{)pnycV*tPz):*W2hYxy8)QV?$C]hvqJ.8mGmESgZ@?y2OdxOi&}nor?t=H[4PpfzP6m+o{*k16I!9]^Eg}De-G*CJF?Elb[%CcDZv]SwV30-}/))tpnZzC*y+NUC6@e+l]S+*(4&s:zuFq.XmMFGtdnDo6*4x8YALkD3Z42*he-oAuuzr81F}IH?$#ElV)/{RX{NeXk&QP0&@]H/!Hn*]h#IR*H/kT#DoB<2XHfuO%p{Fst3xtluU&K2z+ETW=G7>0kBGxAG8Z[.gpqlv8G1!]Rr#TqiWE]PVXVPEE3$:!+BiPK<33+qu%-sc#=?}c+86dle!r&:IBG7By?Y?G[w^XebGCq1c@L[kHJnl&R^Ym7-L$lD>SB?GUV=2?g:QVogf1X{?KNma.$*vh}+0M0kT.+Kzyhg12oT&0/>:h?=T5^fY5$HA)^k)Zg]E1gK-^74F2qm{KuVp9J/=G%a8?<2BrjAP$MDXGbJEp%UwLL5]CF1k>@gt&X50wbfICMr.U^!kzXAAXgi04S04Z[+DXUil2mjq/D4W%{$Uy8io6XuT^9gZO(X[a25otBjx>.0Sv&Y2&3xj!YFI8z=qZcx*Eow]fka^:iP.Mcwa9G{e>7(1$lYgO?BBM8Wj/<2kcWU!JArLs+(zMVUwvpyTmFsGG/@d6WvJ^:n]&+]K8yALrHBd}0zn0hhK>uJtLr&Ee!0dHr@Smw-mIj[.HaZkh?]75li5NV$>}bg%B1NKW(Zq=q7p!yVvXu)-u5A4zJHGl<2eES&P}mK=ek*/v2U42v#y/4Hr]*8M(MQ0E?+8=pyWR]ps)+w?=H8.!HlnlSsX:S^EdvZ=UQV]eDO$WF3*%hWCcXQYIj>c}DlpDBMDi0]GcGKs[cE^gmC(mH&4FPjW9@brn8J4rg3%WH}G11FMm3I!Or8>[U0vmp5=.r%&iQfuT$$y4WITS^:S85oF%9^XBeuCp8oC=2d)S/.9SHECGVezmy?Ohx6gHc@=Xn:{oZEj/yY$#i*-dyyJeVw&Y-%jtWE>TzO6G8yWb4tV7)zI(r5frmQKpLErdy&*WMe$^nMq-SX/$k0?ImDEC1Q^NH{Wy-GOVbD!1o6uV.r%FKgC.5/#xP5[[*v?8AP.ljFFi(!m6cbdsEi*BC0e.?oqzeu^SvT}QfWH>N30+*gc78jTq$^l%OQqS(eCR9&o2/fn2JB[}343DXV0@pZK+-]sDN[eM6oRUmNgyuIyE5O-N(WPUxi4c@qxulPv9qVAWr@-IYD4y$k3([s<1G#-m]ie6>osC*haeK}#09.as-Ocu>^D2HIbpdOY==wm!9lMT0rw.dDbvXY@4zpN4T}x?)*f?Wy6Cl*u]OfJ(0o4igEq43&%bu/x-D7&kt!x5.6FSrrv+#[k9yVKn^Bb9463f@3xxPB]CE}MXGL}&^Uzby^OCq:JkiG}yq/(?tH}zIzNZgD@WGOn1]+B]@jvsz8g+1M{9I79+]^2:^9}4R<.#RZ)ss[7AvUEU]LSy06/l0l^Ld0mgV3XFey<}zwNz(5I>>9Q8l-??qj2}-1@?j}{hXR@MXhgb4I>J4IeMRjSY+hXZ8:/O0qBM4(TwX:E&Xe{qNKw2*sPmTIhg3%e@)**Oe:E0-(!x.jMg^2?-[A8ZoSU=!93^a:#Lg-z/0uG!O!D%l0aT!VHl?ay+GspjR4hhwIdVD1Un.7gZKc@DT^!2n$wwqZ[$Zx4>fjWBeqJM5Mz?N0CJ4ZoHGHxNnMhOVnjHAob>+*EqRdIqoK!)u%]VoP=WEhcXXQF!pQ%ROhNob3WaJ4&ue+&@Ahn2>&ggNLmG2p7mF4hLETT5X?Llw}sb@cF/1@lWFaR/u+iz[D}u1+6yij@r?nwGdkzi.PF(p/Q(e*k.+x>z9$jAUtf%{ag}0i8J)&=oo).uXh%c5ZiF:T>$C75XuYumL{n@]^A!A-OA0mPjT*tO)JF]T%l^{P}4CAAklu1#?2RTi8iFl<6WxepVSN@}1rv&{iZoRsn!p[MZF3!OxcLoi8eMlqY%o)++JkUb9nfQgAe:bqsrJNVt^E*Zsdhu1eZCb]?0<$.7cWQQYjs0&u{81g@&t8={?<.j}?XHr^DUnc8Mw$nPEb9K!4[FPmqEtEgJQejK1cOEe%Qc+j5R!xBN)B:3!Jj9lZ7aVGDA$X?vUqM6SdsmuARoe/1VsuFKAVs(07%ruJpic4D/V9E6y4EJhrSP-2moen3U0n!yS#JBen?DWV7fR[xamt(E+0%F{ifh!BZ%BTChZnSXujbqZcs)?*/.%tQWhnW@Ymz-G*M+O*WowO?{X0Zei^:ydYl]X5RY*Wtfu@v#w>lEA{Ta)yCWfq@j4QJmgePw)-GLeWSKz0$b*/ATY6ci}Yew+g$L6iv#6Po?pkyo?H.4#0bVJN>up!D?#DD-1@>I)5oxRKVmKRs8h9IU&Obc/JO}&CAgb}8<.<&RT5Pkfxr#D/@uzEE$1Vlw6q[:k)k{fCd+UKCTnA4Fku^ZSDo.Tml[MT+@?y]STa939VH:&xP[[Cv4wec)Q.vb]=x!O}226HNtp(/MIevJiH(f[i-j>y!}xxOY3110HXQ(Iegkc5oCbvl=XNF!ONIZu$VHV1=0/OH(K8pk8B7s>N}E:jz&FB!jkUuN@:$:*vN=x*#l{[Fts2OzpSOFcsc}+#x%t^f!*#+=jHrl*fl%?r{-ON+R&o1HR3Zgw}7(9Av$&6eQeZR8NL8+tYs34ru:mV+5yBd.)F{HS4R>Mr2]xg[=FZ/8AO6R^eODMA[7{K&e@fTP:o@hy4[^<8*9Ue!^i]2}yXO)3cNcoavw7g/dtXaj1E>4^jz#o]+nkAZExWwY&@t{jSe$H{/lTb8%1^MgH/pP.%aI5O82WZ0(Oo5ee$6ws!Y8}Pca8z#BSQ>D#0zDG?jIG}!(n7I5!NMBk?)A$bGR%2)n+SX{c%TMEr>DP#z-.Cu:)1#=gge#qSJqG72#hjVwQ.T2y9/OIvM}h>&{WgGv(WQVkzfG0Qa:$QyJ{<0-{![hWizW%l(=C:v3F2E*hlQ4o3MGrcWJApxnrxg=]oC>-(zVVJuTh.h6vae]:v=.lqJj0/9gCWjw4CMLbY9w#MpaMi)1R}Brt20TYGMbzb3lpkjY}TpFLB%}:t!Of/?ONiNZxS.ZdfV*Sa+PNf34CfmeRsxRsrNDPN=^aO2/]Rh6QcCVIa)V7r2m&Q>VURqr0@:X.#+Mo*JT+U}{zcGVx]X=eKi[mR)Za/NSR/m]-]$>QrJ>y:cgv??Bos19te>jTWj:Eld0]<@Z.Sj^l6Sry8IF[.&%4kIBx-%i*P4v3usXSAAjOG=)wfE)U!8iKl=fnfg!>ZNwAmbS/]V)s.w-n2IWLfYSOXNXYnR%^o8CAR7S&^-QjcOvYUq#tTj)/7>:{S4f}gocJUP(X?nFqK9q*d6&h-EevA=J2:{vkOk?9MVj.(K1CD766^*e6/ZNdLz1L5^X-%QF&O0$:OsrwBc)gHc@dH{[VFH(^pkbZfLz&0GBiAaY!(d6Bj6nG]G4!+YOwR}=8S})Cx)Pk(sxe#{T(EA6*Wdui)ZNtzRSWPBek>9zY1N)^ieUet}rb$MyTGd{9^c+??l7oppG(Y4l+@la[se>{}!4v65S4L}vqwsxz.YcFN.@<6Rb(I[$ooAM6o{6Ml]ZmkAL}yycFiNWkY:{:OkwRCN@4H{6d.y(L3QdunNQc0L6$Lp3nW3]x>)1=fgSZmh?W@pmos7Jq=@IxucWnwe>w1{7cNif]lsx15n)H-3^>ViI%4N#YZv3I)7W&vdlfT1>Z$d<*8nNm9SM[uia:I-^18**yDMq1#pW.tEzzQKVE<3XoyC{%=yl3QA+=b1jdV}u:Tg[T>pXc:/%bm[yY#U%l}DYFq*?ZK70c%H6-%Al/1%tZt*O>^$3+Hw9np/)%A@G#ViG-ViA.h^IQXaEGNQ[p%6]XFc&zdX)t9NK.eIxk/r7QhgOpDq(l:#Z[)mY)r[kYftgg4a2}sZPa9mIB.E9e[UfIrbFM3]$^KoqIrh[[4eKX-@c0TB6FY:an5S0x:G{8mNOx0OEfa96)d4(Ujkli-+h}uf!}f}{@99{pI2ae)*4.Q}Rvz#:}?(vUpAZ>a:8UQBoxoB5{3Twr?6nBNw8*k/e7ycP?>9NTE?lC>S:K]9HMLIKp#sq46Poi3SX8K>u{E42.iRc%ASZ7q@2abYEf7nGPK[Y4viG%U&FL!wO!/XeKAa}C$=kw}C5-$]HcY//:&?hUZ?W*Z=E/)cMf1YKNI85ow!2.}arLrMGc.d(bmsdH8J+EN}DG:3ZJ&iRGF.zl40[KNIo{HkEW?s8a}?ajy.7j]9BP#P%t}m2()&Du$B:a5AHnP%&Xs(ke).m^SMCwG3Es(oq<33k>=Lhd<36T-m6ISU*:0g2&M]PYY(u2([o>&dbJ6ya38cltw9BR-&+1nyd@$Ff!%({J$QFL5t)#eQwNti5a50<7beguzW5rw{tq6KF3i6&fp{cW.2dw0YW1V@3.eI@/jWiNo^Im)&EZBihy(15Tdl&)YD0/156Rs%HyP[MZ6od{Ed#35IUuYB8.S}I9}mv)a0tOM)YaI9tfwzjmSD9S6Ty4X7?<(bF]zXV}pYmljrM[.i8)c5amK&b$gmvaIn>jzp^>C-F)^3jnT!SP=jbp/>dTaI<{eg*H]Op}d6!PhBZ(OpYyj8pu+Ly[^*AdR(hEK@tGfrlt6([5:[1O/VIjfM&quuvVB?ilB}i.whY/TCK2?7o5joBKjckyoH>:^VM#Wvr9>%9Cp(cp8pjYy^UAK=w6tXBTzl*p{F]Jk+7osWB!0m-8Z/ThsjrQ*M!pD/cQmEN}JFnVMO!-Sm=:9i*19mpq4)*58FvdIH7U*05TC*^@@cBK}6pP$Qf[0.EI]zledQmi>R*Wq{z}YibqZu!Kz[1@4a?Sy868sUe/M=Wm?DM[NwG+4*yoddE{cH.QmgVy{wd@5og-k/)bJGYdP]UbCQs<.qZ{4&}G:iVW@S!Stpo{qn(ZKzxVesU/0tumC+#%z6JhLjj$ax>sVX:Dt*5ly{w-Ajh&^poVNs[[8!n5/WGJn]b:dD4xr+(KhGp2CT}2)qEOSkn!TmIEAw8gi+uPDVkX0!TXP$9N[[=K8Km^dgoW8X6x2@as#]I(y)Cb^AVAhu{sghqR3ZgQV8{!unXt>}sHzUnoi(QJtCHU0.NNnJ2$eyu$4dyA-MljkDy*d6O7r3fUsq.?eKMY/LZ${G*U^Ej%}#xkah(lGO@5MRSvQE+1n>P8J9>]xtwkZ-4vvu[ee{SC#H.{8l[iI^znQtUuAojXw:%W+6&9c.:}P!&fnMlXur/6RkD(A^k0X/G)Pc4Wpt*YIrTKR}}T2mauofxUO:r#i{Ac:K%KgN^^<&#qSV1?3ra[sS6u=rb1ohfdmt:@2>P5OM(L[PYQUc9?HlBV?@Y/L^]K1&Dbtde{oMBp<0-)OZ:Z>=B[If.ic7.x?h@a]4HLYe42Ga+iGNC]X%9=8{2D3h1ztyKWGc.W@1ojT4tw3.^8>+sjXBm6[p#.s8Anr5$&#{Ql?/ZD=dW7D$WhadS7XSbk-h{H!!RtxHVDu!N$gqcNtk[Sogh5BF&S({whXjM39N+L)/2ynj:^eIbA:]-QbpKV@OVjplY)CF{=]>@U.4F<4*$dHz9$0cvoR1i-wLg=Q{Q3>*O#-pM31LtFdQ.nVW8aGp:Njr76[w9$?IjO&B0-b{T^0^(yZl-6-I}C%x}7iL9tVx@t/*ZBTpyWO3r4Nfo&fLhHLYyiN7mU-mR5Q&4wwV@fgDoi+B#rZU]rfxaQ6m-)mb99+9@8S4r$kogn8iul!2^BK2K8U8(JpU@yDNeivo6mffkprezn(Z&^t!%TowbK2Ewr-wbQ+ag&$p(kZg=3.vQDrLoxR$9AMi6K2u>^wN$!iKg}@E>wGp@e!<56598b7Rq!&E3LG+62AgJ(c3[wR-euZ{>vo5BN!o1tu.oFk0aC5nTw-)oGG{x&4^s)EamW4RSiD=o0DSG-+MgmUaimiyM7bf!!fg8lmGj/sRQ@)qqf4buR3cXt$d=x:zyl[QybB#SdKTDkouJOZJ@aF6*D2QPdFq!IM>OGu:B1wqm>s!6Gtnd8^n.IJ{jaU8mJ!OxldQqx+:sh9$IRyCbJ5lgXP:D9wo2GoEN]Sn0P1YQybBWF1Y-BVF*6RNrIg4j/Lx@irxOU![!otKg{2%I12ry)r.E(p04t+F96/Di63yXFh#+p4$/(.czH%dNosq6b.B-wB]VoIge4uYHE&^#mJK5by}[n.nmzXIIzYtWJ0XV2i[2gg!bGT/dZlZ.>v5UwbxKtgZSvdl=v[e]zlcZdJ8f5DO!3Zb2!B(swzPjS?m=0E%+uZEi/jP#p{EaM/LcJKD/=SUA^QZpWoX2H[^INUZa.m[+YaeEXcVvpg^-M55N=z5t-H8o)JZ7/!8W&T@{28CkF3Yw.uWHqwvbzV^@j}#il5o-qocZ1:tRsE?)VI#eKIz7IAO1T:)YQB=T3BTl+-$N6rh5!SG!v.CLPdHps!NvwpH2UiQUn10m>FxV7lEP]Wb-xT?(lDaT4&@[I5o6VpFGvz4erqb5gRW8Uop+kN*s(nQgc1[c4H#>]2JR40$9OlOP0g?.De5kDh3uFn1#2]PYEH/$(t993p?^q>F:5[>6FXn#jZT{P+Y&QcU:HVJ=]}-]}FD>CkO2g$Bh0e)bm(y/%B4UE[{IrdvoP!jeS64@+QdLe-Yp>OjWwZ&/j)=!OVD{Mb-L}&+eP(hh?E4isDVhCgIEj7OdgU?#vz#enz?$?nrd{E$ekB9%.PAh>1l^4q@IV#%)L=(ohpOs+.)#&f$>u/a%KmSZc=LAd/gYx5+>$mri/+pbrIpx2Lc.*$@4y&fdSaU.z^?Nrf?kKrVn9}ta?-.e-{3&Phb3i)4e62S?s#+iL@$lezKP<6d7$x?4=bE=S=8nQLn@bSwM2H=bC2Hb-R$YUy4oZt5bFpKEQ%q7Cg0AP/.r$)jZ?WjqFLDdN{jDyh^:*3@?M>t}l+9/&>FlWXzfq(!yS-@ZU-Z*{$:Y58-?v3ONprbsd?7AF6+m@*IYz*{H8}Fr*v$#:6f*?i-bT:85cOtj/jK>RsAXyxcPjW}qWHHDbB&/K%LFpDAnxKR-vyHRXZ]rwZ@?Npz]:jaQ%q*v=4h!aythGbt9VkH*&TU&CH:u7l5*CxSFk{&5#hdIri.L.SQWMtQptYF!V*9((m0KuY+zJ[TXEBFil(CV387JiI[{.6Ok4-eeWMS}5hQ1[4Y8Wv>(x@/UuMCS(c:HE78O}jcAu{8=W{8mPxRA})*uzuvndVyP!o/>p$3T98XG=seVc30/wOq!p5Q:J!IPDbUko$ryTE#Ixe%lgA8J+*BA>]-S]k.6TC6c@s.?!7QI3BeJ1}l+PD@L!jJZI32xKW[c>Q=-{t)1AwRvYM>mhuOC{6g)K9F5BQM@T]L>>n]uD))%pF=eA]z}0SsXStl{^qey6V?xV>i&+}@syUSmke9Q@*RPSg}2e%96F?5S*@>?0d7k#$--:CEO5F]NBbtZAk]Xmutpbslu!E+R950jDaXsfPfs?HN}&ahGc1/WITV^-QKEx!!Od:!T30*s4lS=:4#m$}%2[t/Ij5Hl(34MK+DxzEHMR7:jf<[IEs+z[3qF!@cD@l5}{hSP+cBgSW2-rI@QG!8GMO4%QJNj$pIHAo7wb5$m@{R&C7bqtx27lbSxGSXOF:n+R}>x5*z[lIztzh.CVP&8t*:[?w1IY$IM1IKtfaY74N.+&q5Lh=mTIpjzc)V@{SSF+M(D03qt2KFw}CdWRP9ccVAK(*!2IzTsmL2[jW(wAvx*6JQz!&v]qCC>!j4F{c[^lWgEpNn-O?t6in^Z=J+T$qHl378CO2R)&(aw7^vk<7zUdf%lJSf/#!STO8raMV#KRQ0TZXgg#$LwMF=ZrEN8#NXV!nMz$j.cHRFGu3+3$3d0L3VpcnAkY3&?lTx${yjeVj1et6L8h$W&wp<[A:y)Um@P#)JK&D$YHlzs6Lg%:vge^>GNRR1QZ4y]zo>]V5N@BRy)oE{=^y-j]rvFTz13gWMi{t*3(ng5.3@Md2p$--WqB!EXu^:*oz(dic!xb38]9]u@BL9kxSZx6>84o5Z*}Vao=Ya+L}96J&9T}zZPU0{0x$cgJMmhi^G.^]tfRMi3A7h&TdS#M(>SAbx7&l@RYsNcIV2-]dCB6B.C$@4%Zd[%>Y$g)U2!^]+MB3jlHsk$uq4b[=zw$+-U7E=Dds}{ghZ}3Pox]wT[/)r>WMZTEAOpgk[zl=95w@d*l0)}MhoK65gz.51JcFaqBTH$]X7KDl17ZvR/e3?BZ.1baX.Rz{FRX]>Hh(2}$>udTkB}b3^hY@B&lb75(w4/vG=Zna{U7H$ay5(Up>5KMfkGg]yUDOM)E&5IedIphgweW3$)e-k%3U10cwJHC4w9^[0W?fR0{t>o78a^YlO9[PL2O##}K-go:vF[5Y5MY[y#qy*L3#pIBqkySL6-M5]=ljT5SYOi++TX-1VtHChQN$Yjcm*mSkmPB5<[Af)>)f@W}rLUSRo@]O?yrwGiC@TPc/Jt6}9?}lNB@a=6UyyZ8y4${7Al(G^dILcPI6zEsJT8N]hPH@*]bz:CqGin@]L$D&x1u.hk<>!M4PZtH%?pRVX]+B60=U@+{TSJ+V#nNz?>w<#?H{ERnIn&7snsI0yXiM:>[.C3]xekhx5f!71dlc!?VgWVA^=6k9OEy{Ynt^7+5m6TA9?i&s=KwUDpD#AeZ{edfhiea$ytdzooM*?7af=-PaQ1k*&3fsM%!E)p>2Xysl9dCPj^/?X/@lv4NrV3:xB*&+a0v-QpLR}TYfyr)!ShkQs=1/S[s!mZU7E(&n+K:B0g9S7E=4J6P.z$8XJ^GSm5z(yAV1TX8Uh5B^{m>K*KSt5=&.J!R95%cqP$oz}IRkK!Hrj$-Bk{5?->o4B]Q@TF^@si^!.jyt[>XGUUyf3va0KDMLSWS&U=J%GQ(!K9+0/CX7I7i0nlmZPl7hROEC1+p=NOPSq}Nn8)DP!TWSvP-Vx=@xzIX3xccSgNognJ39t[IiiVwG[(-C}NT8}-tG^.@b.ndUL*-^o*z0R}zl!wJ7MBUbsi=bUIF%S-!uoirfs4z5I}}>SMOksH*7Bk)Mb)!Lo1@-?VxNuf(>lMGrRwH^}1in]qb34x[EAn9wJR%Xi+ep-XFL/Bmi@t?-oB2U-ZBaW={{1uk4+J&)T}vJhdS{BgDFdg?nMju<}.RaBdP&?+8w&RQSpnD[=p{[Qw).$oA9TzuqMR!3gtSdQ55{@Kv6XLSvuqNJt+lpIn1[f4L7QINbP6gh]sz<52-]]EJCnbeIB+()Q#o&g^EyzKz2t?}ZO4Grny5AYd}^2][:@tG][)da#x.a7X0&^U@esIDAYQT&$(2Xh^$ma57UQZ1E6mwlc!WdJC57po!(-F52PE)d]bsw#7(q?/zXDytb+7Q-G/f40W$zVwF*wIawx)GI53xeZmR]NEqQGa>fG?3f6B$y^&lfuHRn#p5i{f)%IgJGov[m39a({:cn5/<}zi[4=:t6d#BW&wSP(}A7vU$lF$pBa?UhGpNMk%+):wH+72PTAW-}:pV2qwVV^!n6}$0#P!y+GGLnT^DOdgfM-6H)=4=]3iw6iM@Cz+ge3#*m*Aj!s=:9Ue*?Md*Bs{JC4q.*bmN7%rpZ9Gq#gV!5?mC4%wS/s!]zhSSXw8}U6M$T$XwafBA+U9+***YbPlyyG:Gh9Roq6mj/(Dsd7j)v>R9#3-[!jxXNj=s{WbNujHBtmpCHb{08l1H.Z3-ifx[C9!x$wl6tk=aoZmaYY}GMiUymY2m2{FZf!6c5=+5L2mFc[wRLbrKeQ@jYD-:H{cFZc2HaKav=4g(Y7UFLcenZc0)!AMu%{9F241%FDb:CgSxjWIPV1}5fkkrtd73K*+^ho?^XZ}#8u/#5E[n}LeMrh/@?!^.dYbyR]w:NkTr@Xf?6fHJiE/P6dPpy16nqArqe]Rr6-88M0HEdTXWsm6(5gmycIcllMW5!oZxgiq[j/tN8b1oAFK3@HYOH01J9v^G5#qe/78kxdVkG/@&@pemuN8Kztu^v1Jogg}k}-k78=BTMkRLlyTGEQr.(sf:-e76haAOb{vS:$EG:F6A6Mo0{cXf*>8!M+C^cAEcuAtW!qBKIcbJYA(t!S=T(4UW00]36kV-#cLC$qvi?1B-it{4=J[@cFD5sjJ5jNZz1m3bC)u!j.^z)+(s^MACHCETRccT/n0jNOgj>8{4{WiJf1N{]S1DE-A5DZ{kC#A2rF=BPEN%WQAe+eOV5D1e]/W^9qX8o-J4*7)WVPVIM)6>H*KHe3VM3RpM]ja%OCFkKNQWga5lNHJ&Mz@mztU}Xr&$Fj}I2-3]3GI9S*19y4KS5!(%tv&z=xT^}F1]t/FbrR0qQ59L>dy@EeqjzKbtyQvhdzohnPiCUIGJN-#?>^>yf6AB3Ob:10{nP)IUZp?ohnTGZ&v#4ZJl*lTX0=%/zaa&wcf}Z^Xajj]=0rKF{eIOpdjkJ2}-&)GC.gxyTU:h]?zJ(Aue}B4.n5:(gll(&jt5]?z5g5OBMZr.+X26ej7XI43-ktX7>P/!K0#euxGAal!A!l58e:ydScxRdP**gFe[XZU1!f>3FZ5>eMhR..!jP&myH/I^ST}^IA^YAlY1b}EJ4VS]iN+tPLy1srsIPXEjXb=5bl{0UJ^K+Rg^58]2n{q5pD=mm$9cg*Qy)}rjh1/5*1hSrOm6v*y&ZoUDT)m8>o-6}YL{bSQ1&9zOnYbiSW2K^(wO31]>yqrWxt)}<7:6eX$T9jyGZc0:8RzM0#IZQcY/QwWk*sKv#vPXqe=+udKC(Yr!S++8:5YXNz}f^SIF9NZFKGAEx3@3Nsp=dp=NQ#90N^mOZ6I+0}sl%:@T6B[+Kkwd?B+elXAoaUkL(69OxVARzP@o]b/AZvw!K!E0r)z2RiInC-ni*JGD?P$r2Ab4x07/ZEdx5$@jM8@l@n&1(u<(N77Wp%37A4DrMk8DS%zYFAuJ5u@W.z0V[?=f3aR8g&G]V$86jyGL8.#hy%6utP{<.(=BcrUjIuyM5YqBgl5GB*kvd+F3>TJEZl?dBTZT^3yC8bjoh/tTH=>islSAf+DU:G)xUXVG6R.vKtDWyl*N9JAa(j!%HB$W7vl+uB}rfLI^xX(H5Pw/s()8Kj0M<:W{.P}DSM(x1-ALu)/.Lx(VCG+ZiX&=W8]iG2E*&L:OGa91uMB7Wu8GUt*D!N7{!$z.#oGW*8o&E+g%ZmcPxvf{3y{bFxF5cs&d?^KyE4-&xsYcSbgz:hqzt.nm-li#%DMn5F5&(0viJ>4nR+N7Kv!)E).J(c#{Y(l8V7kBd]W1[Uol+t[FH=KtAnEa}0xe{7NILt^K!h+gVodW$l^%g1(w]T/$6}e/2=WhH?P(Ncw6}y*xy{?o*.?oqWd*zbFdH5$Ga@1z9A#+R}EwTxA&Y<9r^z.eE[5#SShGHn5wH9=]Dt#MuU)RL@oQ6vFCXis2{oO:gbTE%Nv.HCOcPvpeS{4*!)jYdJkCorAD9{UXD:Q)4vy@*7k?yN1cd@I6PcJ7}6gQfF#o<(}VOpO2CywjeQI3$}8i-NdX>uChiOt5]wo&pU/x1!a:9vyjPprh46WQ1mspZWSjhzpBGbJvRS9UmA8o[tYQeLKns+WHzsxkxV)g1zEPfOF{L:pstfaxyH{szSIu+f-CLEyM(Rk{S[j#Zw:A4JD7-}*e+QR%@5(iZ16r$AAxa-m1}/yZFbIHu-84c>y.eFps6#*/1GoV^8&!f(dOPsYS{O]j3j[K&QK?-%:Z&!bjqT^UFBb.=T7w{3L3Y]#}hgmO[8w^sb.v5JeRh}.mCv)=AOjDIE{i:7Ja5d[>V7@XXprtiiGw!w^C!)o[ufO.jDo]XY2r)6DZVN)8Ju0*YYSXbQOzmoc*7=:MW^>*Fo?[vU{Vj7Xou^9jyMoC6s.<3H{Trx.tUH3>HkFR}vRsBpT/Yk8TIQ1ClefEUvXT.KHbxTZ^xI=@RwH(WxH+yTe7O0sV[.Z%?m/cx5jJ@a)pEFD)k>i*[RB4YjDUqO${cKZtZ>yz%sU%f}7?:c}xk>FhfmzEIRW)eZ3@UbxG*]S=He0=LG.l5Lc!3Wi=5n3G+&0{)yIDVSuxzZc3{Hn$D:vZ4uF{c$b28k6#cx>(o}Z6$:Cu[su-[pYg&G4^QskcWR*SGK4:*Ka!P=&*LD)?k6/MZh%ZhZK8n1{!/bW/:%z@^x+nVMn1/[m&=*alK{>C^qPNqBoi{QFuSF+J*nQ4.s6qbZyAPZ0g-b[oxWG:/m+>R#wpUvHdS3]S:5&EYAO3g9{Y%Jy<*RM)Y[dS&&xOC}TDQPA&?FDm*E<5.}Z**mR<=k#f.{NX6PG?WmA4}wI(PK1N:=:SfBt71o{G5XVw=0E<(y1y}K&q^]W}+%I()FSXCqJNP*7(7AA&xEfqp3AS{0D$.5PF{e}:.d-40/ilY^r-=(A[!R.z*s(M9ZzU!{r?HWg)u[+up7/sL>6yG&dhp[*R!OFcM%=8gR}x8x8q(rGnGRoXM]]?*F7J/vW29NlCXK8evLMP[5auoo{%p7vu)WrTPjjO%H{%U=H6}mCTs[LBnQK}y#]E.Q4iB$1[c/9mz6xe@h7f1Op>gy2{VJ%mVRsf{l7)}GYSEQci.Y)tNrRCk6Mtxa:t:AGwoP#<%4mgWEgVrlQF%SRCpZd6)Yj1SGR*:fB=NX.+JbKCBexf=!3yR8gok0[cUV<3^]g{jfVW]njR)oJCXO>cXP9Q(Roj?AgB3Z22/h?U&Lr7oKBQD(]0*%@^S%1?5[6#:?]v3fO?HjfSY&+$)x3x@l>{km>kU]Nh2dE54H6>0gt00W[u04KyegBUKlxXC^X<+(cmYC(d-q[O2>&W^h0o#J&n?]%N)0iP3X&U3[vhv8>GCV:gzIeq=R6qaJ5w5EnwG7yWA?t^pvsd{Rh2z2Mmyk6CgnjV(hJYrjI/oO8aMq>mZm4#H9nniIT:YK%XF*L4JKq0x}$c6=HYd.VHDVgO1eA#A>MFebn+4A=CwyX5Xj0xrWptmgbFJSiO=L3hVc@?VIr9ws6:+>Ik3e9I0qpsF2NGd?sMEJ)(x]#zA%jf8{[}JU[q5fJ}grmK}=)1eSkW+h[F?*ky98tV-nU-4U+S8P/caeLd/t2WRhk0WI6(/RTKPRrCm/ITp.G$eN<1Z3-r-JL[A:LA}7cO6ris(q(oBo-O:[8zg.ctdiAFsOoU&u4&dLLzodEf6bz7R2A=#p%8iT5HN/b-w{T5C>:#@/IM%/7TpZLJgJ{&X){1@DxKNFe&hArG^Z>hX$p=vG2TB}=2JNi)9X5zGUb)GQdp7ZNjY}PG&wdovSZK=YvXB^ndr+9SqaZD%s3!:jlse@&GELh=wGN*f/YMYt[a9?SYAi9W5<0Y7K^AuG93zkGsiC!{n3tXaar$a:[LyGZikQi2NITXfB.A/!&A>HPbk4AtIXmJl8m+xgFbb?SO*L>(U(j9:SJO*1})d^r/pMLt6Xy!=K&Z:oN*Ac5sxEQN]5=PS*Bug27wuPXVW[S6*:wIC+CJ&Z79IDLDS@s!&t(F:!IZ$l{@AfGBvQ30x.vs$omKh]WxF?&Jeey(Y^[!E[{ys!}Y}7l(z=}Z&q67s]>)G=KW#)sPo=d+g1G7>&*ihQ&XvO!W3E2wZ.x&/DWUp)@CUgy}RP4(mb>9)V7TZyL<9cTP.Pir9tyEEtjiD}T+Iw+crJsk(/2tqxumyd.bcMSnXOX>48u@aTt.gXVCH[$Wg^jC/i{uDnMx5)<8Rf}m]ONhe=glG*als55#bP4ze&kYc.!#G2yiTm6:=ICk*^UCO9pi}:6Md]hm!w!)YUihSfr1^kM=x/Zu$Q2n7/Dg54y:A0AL]tKso4}$sM?{$5+uVzG^U8qIQExVoeL^Cqf(pYt&2NPw.epwd!m8q:xDm?BWYFgvb8}2vRujnLcc??cZU)1:JYB}Qy.[3MX-qp?+th]&h+*+/*DRd-K1U-d^Ro4eFv%zS{uh%4QW]Gjn{b$kz=Ob7Xj5sEyKT+o8A7L/XLV>xPrgiD^.TsE>IN%MP.PynbUww0op%t:(GX8Z!lT5@tWLr+7yvOULaM*]S1h+e)5@}^eis8!P{uKCaNpv5F)K1=s#Elx5ZpXLeWwu@Tpa9]vEDByKDt#EPC1Fi8s#k=p$ZHn!XKNq?^Gx=V=pPpztmbrEctOXopBY96{PLS+(/LupoyG=Hq*$ur*[6F&BzCtYbgTWl&M.=fa2>ItI(Kj}2l(!N{VyapTeA@Ey:-e>.^2WHvqoksQiROYdGO6S@ORyRJb58Ow$Og5R7*hfs]#:SrH:L-Ro6R5@O!DequZ5MOegt{u)5jNM(D.so@-fG#WhpilaBkYUYuQ9(*LFq?rkXsI+#^ezC@(Ud^h*ZQh}(q^b8s+2GKnbDXqFM3-T=1I.C6js6Gs$H$zHm>Y{3%1s9[i!C)wi=QP&}^.92gvSP7#2Oyj7#A2T7$GE7o=%{T(X]?bqc/JkGrkeL(AsL>QMYP[85Klum)hRmby!WE8j?ej^yi%D/-dxLYL$#k9!v%EGwTG=tRW9]6FO7)wZs5u?](Rqg-fOLO:b3z[l(w@U%)3)+9=W.r2P6eQBJ3==2Di:XGe$9HuLpYP[Qu%pgo+vj]U%Rd9hrDG71IK3IqGu]uV!+J+=9CZVu<(I{A9t(}H$)[yYLl=&/]zGozfxv/Pqu>xJw}-@e*S:[N*c&+Q=U}z}p&RDLyBExK:zd8x&Gng5R)wMT]4&hm7A&U#ig6uo?P2^x%g%4P{8LX={]LiSkVx&c}dNrEx$Y^Jjytgx9x*NzNL:OAPL}BOyD$tDfqD9au&]dX@1Np+&K?Z{XJ}t>OKN!ldiQ&Iw&xR8-4Jjk7[W[eqURRgU7JZ)BSi@cplK9&wsE(os?Wu[D8ctO#>(5jlVm&m.:?9%}P{sK.h2LhmfOIPOt(YIQ@6.1pcqp[xQZbVFlm[=9Wf961h0saqz>)+#HI/F7rE&/Q>7JK4IX3q+Yu&1JfqTOIDHT.hZfJYD0q.O.U0gYvn]/EWBtZojgspgKiMnLP{0CD)$xVpo%%MDpp$a)h49+rpX7+NE&m#YeJqYEFkj8a)rynau>S=44}&t[52RJiakJE>u3l>j:#:tutF8HQJ4Jtgf0fHOUl!5WFq3fJQs}?nKYa-&z*k?!0Zn!AsdlmqL+WP1OF]x>9KSdj70A8iM{lim)F%eU?+kkcG=2G(xH{GU?VdM&tp9*lEQh4Yb9K0otpY{X1?A)UXayY77kGy^i(49:u@&fdR5*/]w5[H1h00ic?w@L+pFFa&{)7ry:6lhIPE#v}9<(0CHA=KpM2+.XOuh7/-p>&f}E)ISAw:KFW&lD6cSc(:aGORu4[DXI}WNc>[T4XEN:G0U/(f>fuU()@8utUo5lM6S5RBS:?zUL%@SZ3tM=Hc]OYI#9=5%k({fh&@jd.6HUqay9D!!1OUCLZ[4uWTHb%Ivws8rtA4J]}s<^uKSF3phpQp:/(QgaxuFtPUfK8)z2qj0S/Gm4IJKb+m=UgFf3/c8mU9xnN1*:u+eCZotZ-k9+ieL7lwN$x(djI)tsHjk@tV*iCxZ?>2qZt)#H$66YfQ0eHU)bA+JGRUVWw:L(@rP=eCDEhO?y+Ym*?dE[F5}W/UAn-aTu!-n9U[JW08LtP=-bpmkVJTkK/aFD73G7v-/8[#]2)OKYjN!iil(PDCkC:F]7l-7@wML{G8iVThl>S=jDrjN+GgZm4PqT!$]X51h*^?HQ9pLQD{tni.>eVyZUC0IFJzMFun1-Fw@dEvP(oQAs%5{[QhDbX@]IP=w%8Dr$2Lvqht7UDmM}lPfpmxJ(Azm!$bmdxsD/WrK*}j]bSbmded[tXTrk6WuyaX/Pis=!ShZ$L+z(To$^cSn@uN+wt5U4dhqyESn2Fo53&D0K^n0SGZrjnnM+ZB)@&i3N-g)SXk!Bq#PLSv^#TXj!T!W9fnJV^Dj.&}Xz)zb4W/>]/6*ty>eVwrhb%MSj0.EU+cgEdK[&[LmQ{/q1f=9qtytLC#v>(M:c?@w!M9#?sNO}LB+2uO70zZ={tFHK[b0y1Rfd8@u$MOMm0#LVt/yxtY+6Ke[sl<:ajKMguI+hxoAO-(Lacep7csav3OeeG*C6xxOhYQ:bvPWP/A-K4Wu.HVLp!h:*ye-RGMNX8D.Wtbp/KQ^D>ZF)9J@o8Oow?9ICLi/PcTu(p}Vu.5mCVO?Rb2@HtK9+T-k(^1ekg96F0H(BKT$Fj6/dR[(7AO6^}p:M6=dp{J}:FJ=0l+dw1t0l2O@Q7s&Smg0kLz+VXOp%v0PSIXM8pxM@pFr}zJ-^ov{45@0d)ZG0Lu**=Lvgzk21Z$K4aQa-Lx471yNO3RYznJa8[+(Cv2{0MR7n))YYd*8QvLCDP2=BXpq^tz?qYtbt<5I#orE%]33cDdB===te{[SwxWS5@HKGkL}p}O@OgUv+T!ljXyNlKZuV6^5^0MukpWF?Jn8R+iUW1l%5(]O=zml-K67(*:v4r]dAYmk&G<*:&63KRFh:^!^1uEZ!bT%:JVzKAN{<[V/0Eqi#E^Xn)?V6jC>qO=NH/^*PF&RuA=k=7iqW!hY#gE-6W%td@xFln&jR!CP/{n+i=Sh-#6v(p$B(VezWDV7yVk%8$I1c?6[xTBg0D^Q[W^OG(E}fKiJ{=il+q}>5y}uSx[8RAQC28eLK^UKXQh6cZMDv-t]nM0(es1/r&@[HbrfaiC3wi*I-k]RsN%wzD[?5g?=R1BOGn%LbWG*UXR>o@6zGHRyiz[TRrfgrIQz)p%N0ugM0GC<0PWwnrPg7AsHS{1F@M:.k5r5A{07G3K7QyZbo>.T]w*bO(xb(7C:er<4us{pd.*9Q2v3Px:tEXp(Iz-{NX0@/${HOWVTxmdY-?T1>ues$=7d:CWTotI[*>!8j6sKh7z?dP+A@ldb8ASDGMN0y2s>Rwwe*tF>4#=FsUW80{N(&-i(Vv6YGBhX#]^OLpJB4]V07kmR+qz}Y:0FcqT$!=ld:^CIpX14?&+3!wP-=weFgJh=l6I1a]Tr4Tyg/TI}kQ(7NYZlfaF)/DUgZ$kp1tSGWRp3d65GY:u>Nsdj?}kX8TUW^/?[B(4ZM$K&s?zYVN/rYK{onpViNMU4=La*f[r?t%+/R^??77grPjC0hGbdP{=py(UY:c5=HY7Bi.D5IZ.gQ+xcJ}O>Hv.d[{32N@u&n]LjZHA]]=t1@@vv605bKpvtepYTl[*B5Im!(vscZn-[x0*iOeGM^]YHJ:.S8&&)wr%)b+&K]eic{tzJx8OLIco2WGUITZ{*DnnUyaFp58u:r/vxzUIg><$7JVb3#c5np=tmjZmf6Xh[:[3Te0y:SM-:Bu%mQ!&@oJ2wLS1EtDzsht9h3axbhm7}Ttq0DZzlAvxjGpF^/sTwIbANt2&uWy4R3){k@9()Z9WuN>@HkKT8W*FOpbn{4M1?ciK}4S}GYiYQ=(%%fo0}q@tls1[!PYRWy{oE3/OSb-@YJ^{{.yXN0q=8]]Y!bzbT:w&KR{lu[U@+*Os9.Td$g%VkA)-dS?dy8v+)D+cN=CA&/8y=bT^1&lh]{C%xFlQ)i&izXvu=3Mu8tP)N9:e&uz6DJ?xN5hNao]]gxaG!/vMsBez>W8S{s4Oi@T7OaN7En!$?Sfz5xV@7GUI+97IOv]DI}HJEBE^a9x[Lh99*Q9VQvu[p[dGy4Hpk1kW@*2bnrbojE-NOgo0w*P-p<(OT*Q?6?WKN]FojP:}:H*$iEo/UYMk(uoROT/t{zsmhOpt-H[TL6AZIlgMocIbN)l5n+phd=cs3tKP}j1*PUjiHI{%IMW4nG*0X*9ilFUfeJUF:]Yh1d^6Hm4BrD&ruhQix[DMz8T1tsoUa&]by#1f/C)?w{1c=ZF?vaDP).L+VFL<=cBp0-kMY=x9D)E$VKr/zCF5I=0iR^nFs>>k/@C-bFdVKAV9AQf!Bs2^[v)4kF!uM)wFlp6}h^t7Y5[AB3SG(G6BaMpz>qp>TO1:%pkhXNHg[/N8hJoR6ryZ20wnq$PRRbOdND^Zn2gJ8]CuN5BcnMRZq!.hK>2dhy!m{GJDRFo7y6oy@NeN]wIcV12Zim-hh@xhLb.6:N^j:bvnwtbG{y2Z2@e7/]W)1:k{}fo1-f@x=$QWN93![@fdML-g1?ik4]!5f/6ZiWElC^0F9pUjB8e]h{lMGL+NY{$4]/NA8*PTAT3s<$Hf:aBxp^n.zYIC+./VFbu1+VCK^^CgYrg1qyA11sT5dD?g:Ss#p@s1](^OmT9HtuRCPRlv<3rE-OxEtfZ(/f}^gRwOMtk1}P=x3&IF3G^Rtsp{1WU&ir&)f1el?PKO1GKi82uPS5/uPMZRy8P?K9DCqsddI4e&B*zxbpx*v=78#XvvTdQizxfFO})O:EnXa*ZMsladiI>zWuSolQ6k]w!#03.bz7jbrStN[H7s3?ST+g(s3rRP/cYxACSj#Btf5oM]uI33}2Y0=V=x[E^wovR&^R%Kd^Mvwu@=!V0C8AF6#6@)62)k/y7v{8INvE(Gl)0p3fR1]Tppte{ClM>^%#}V4.zymB.Qe0PknyC-zda(NDazMID.:Ld}CJqE*(fl92gDM78Xo]4SPtyzlOrDNW$zyBNHBV)Uq?aX<[b1W3NAA4B/t=m<%[OqjOg298QKEz8>gs+xctN>tX0WM$e8RvlcoBg%0$^0&*^!@B1bac/++B+/R/LS^v>L)9wg?r1p+j92@u!6gSOAy3U-10dw<7f4iGk7DI6acc*TJ%DoR[8Pwrwc.J)Ch%jH=@xkSEDLtdiq#*[uSpa{n956cSvFEIsr[+E1g@$:Qq?3h+]4ivjMn$#CNG!>9xOXA[=mL:fcewQJls]PfSqPR5H#^S8]aqFe*fCJLa!f[tqXvpIyi+T3Tp?9HeV$XeLR22q7mOcS}Ph+)bS3G<+fnR=g#n2fm6uTJmnGJrAjsBcK]^pw}w6>L!X)$)p3Rr+?t-?%!k&92isQ4Aw-Jer/2y$mCU::AS.z99z/vHiFqolu@[TfzAQE)d$e^]g>vSTk}ZBBf(}q>W2cZ1+L%)jsF0>-[o:>SM@r?A@w[qm4Ydh+b{-C7jt=go(oRD!ulXOB}]@.3K8OLk>PQ/cG:KBgRFqDZ)OGGrqOF%Zr20gUvR4D1go8nI=Ina7S885.2XPCx!4mIwN:nguiiSy{hgqA.6=i7gm*{(wtmMpGs]uqG(F9A4-.?ALMtx<(&TR)RPKh37rdm:8TFQLz5l:o}XC)9:(?D76LoIb*UdP9u!s8bu>?U6pLh1(x+>ZKjn{gZD!:+U23q4:u*A=3hO*h[7Hr[ZEvhH/+PDOCu[0?ELg2oYqV?D>:n0]Nc8MKRQo!7{T=[D={fKRgH&t3x6NaVVTD?WkTK/-j[BF^&MPZIh6}t17VNrF+m!Waa/5$<&Mx8Zp<>>J6&tx}jDlys^5)&NUlL=FMgy*)0L!0lk?4u*H{j@t1j/3(2=(h$TFL?$CneE#MU}ubLG$XT6Z@e]JFhU?]p8Z*[RcN:7ymA9%h}&Mad-3bPfq:*=A^u]y>3m0[#uIVj}y$62F+b2&Cm)GoDx%aVkO)0?TnnF0%q4J^V>H4axk}(=m-[(NrA$L&yS%+y}DN}DFEtAMM{[N#W(xMiZCLTEHEChtQXZd&E.dREhEWEy6-ZE@ILIaBl@Y9OkC{iJ!RmP)O>0u^?GaBvXKfu^7(H@gVwZo.#4K9G{47L}=Y7HJjU}s&Oj.[V^gkmBel@8rOaOBpKXq#3niUk-M3l+F{jyU?#]&m9Re(bxvVQQNO/Pa(N9jr9/9?Q-nTu]q9R99:ti4JMbAcC)nk?o}9x^j^XHsNE(:zaOT@15LGpv5CxOkcsvZnbFBG}6ny]bF8]Go%CQc]Ahjao9@!D/3Ug-I0*Mho(wQ7wHJ*]12r?J9EO=w1gz#{Y9mdaE7EX6[eG8F78&){qMnL.3qF5yxx8}9sryCA@Xsny?%KV^{g-ZQC@M/rZRUDdv&w#{()6G}ubyfq]cAK>Pn}lb1o!wWA8Yi.^*]jdnGfrfvFj]a)EI*LypDpc!dyw5g!y8YmEea2qjR*rBUObJ5l2X@pC70lk^PU=lr>lD435)lU/2UP9UsbbYR7QYT-nX6=vEryov#/>g7f%t{^Z[+V1fGCG8:zLBPN*AFYdDBY{(}IU:F&9Y/Ju]x#n95ZMN54}UDxZ^<]iv+yB(r:m3r52.OC=IT+/x^#5BEncHH@g&IjO4ISh+6K>jGQu}dLG$-X}jUV(?Zb3[JA=BKtDx-QoaF3ZLL?HF[pF5xM8J]&)23v}Sozv*IIX#Gf{BswDH+pVWb2Ivm{12{JLZZ-%mvoAR)Xq)e&QYHf!&C8vXJ4-l+%!3&l?5h)bCZq]W-D?SPl2!]I4m^-6apPy(]CpY8I!Vto2GNkT99hG&f&&3-@uX*m.C2l=2[?ig%{tt.tdn7M<+HeYUWR&rrZFYtfhz?g6R1Cu%h3J{T*7T]Mw}[DaqvPRx8EtW0EdE&QWNIJdTu7lr:8sc{K>ucaY$)U!t./N%3FSxZOdpcZxdlJKE.y[qZUGA]zR>>i!Gyt9vmf6Zo!aK)Dl57CdE%0Kx>?8AIp>.?byYf&gG#gA{I&K)}p-gnj+x:*!^ED@:^4?8.Say)-j07$}+XgOqRt&U.1pd[6xX!VX/qO}Ncp7U=ZZuZ@J{g{x5258p]-zr4v<3M&.zp5TB+X#8H&)fUXc1ZlDkM{2TrC+YR&RJV*U6@D?)zf=E(zaFm8S&hq):3DQ+a{4%*(S2SfXQ%7R=W]WKzT}v0u45hx$S658e06-kex-+Kc8%5e%pDt*.{)!i3lbH-u)4P^}UPrbXur/!6mGq0pj0hDMVQl5y5gzOvU*z/b1cy?@%xcNHROSZ%QMd>xf=QfHqRhr]Nx$+&STT!0+U12i*FKGP3Wy/Gja-.FSOB4N%TJLe.&uJm1c5ro$*O9}hR(ml5L)>rY4pLXKcm?nEjk8GP<@8Bzb}jftSoY$bcXOqJ?ZAe)?#:aR&I5ibf:.0C?1)P%.zbgzg:pk#V{>pw(a-SXo^@.<44hZLS0YXak#uVByv%kN7jk)ny9d>{2sQt^TvgbrsdmvxQ}BT3A0y7drO!x-iBYS}XVN3%TY?V-NJ[.7X{a?>BgVx}*%LromTe@DGioM*Ka@aT}86?cF.BsS2R!1)/n#sr.*#.6rQ>X@#@+QFZB[tYX7!wMPX!u4q!0fc9y/)Z7Iw2CNsF2h]NohnJ*]3{!Er?4nm{+FIsiS*v9+Si2ltk>h)IImQ4(%z7lyi^wLqoY+^T#XU.0TZ<.r0u?xjls.kd@4$dNgn}wr!oXgI@i((@UBafCD{]W8wnj{YA:pU90]^)Z6h(g]5HKJERdUYPw[boSTbq!V.4?^gG!.}9zs=bQFQ-TRiA..oOr&j(IOsYT!p*Tiyov=h&Ed=Vl/b85=-OCT=y*6<*=sJQp+H)2%4tFik@A/Zvpx{^Qc{.J28MJstv95CK.Bo]!5Qpggh!*+4<}h76s$F+z}&:F![qEZR=4i*1w2D&xRiQ!gUO*)xEMrHTv}QgrP2LFHP=1SsD+mm-CF}4bzNDtezxo{42bKR2V4sL2AcoS#e1q?eGo9(In+]:y]M%@Rm9%ESWS@rx}[5}vJw)?+eC[#QzEkPLVa{HBA7u#Td0dew+%{[IRHtQB).V$mUXh25p8l+or1*:WYeazSJv8MY>RF*[yxy[](S{S!]GZ(aP[#5jMz24nDp^=j=Y5DVUTAd@FF6sQbUe=%FN*F1<9IFQb[{LsgYn:aTe25k$sY]28iv3@%]..M=mxo{{4HDZwBP=iw:nuLFUo+(lILz^v}Q=e{#^[6ie0]}FU)AJLL=AK.n[@b-dAtFhQk}1!E.[jKUg{XX8kI$E>F+8Ys^KP7HrnG9=.*aiD]l/+5XL}1#zI^ny%U:Vy=zhyauULax=K1%+uD!Gv=2bD:BVRSpEvl0T!bmBQt}:]L%gVOY)m7lBH!<{:^Xe&]l!q2%XK#9udajZjx(MS=Pje!e9m-WenMLG-bBBJLE/!}?w(qf28v.0?bQ5)m71M=>9A%Xka)tk2Q.UQnqjFv5M)TWr.):uIjimlHdAW1x++Nm4#WSBtLWR2T-:=Z^C0Kfflzs&kudPSM.a+95Al5q!P!z8<]icUUgyUx.#YA+4{h2k+8Pgl1W7vNPalI*%O2AjfSOUaABE:!zwgUNCO!D@DW]ZQd}*Y7.K(=Ften[:^yxM3q/BtL?C#s#tuVuxU)$bg?5D:1OLtg?i[X/8t44QuO.%YY/aAX?LSsV>A(H4p/&k^4Y$53F1#kKncG5[v[-U}(}VMn[r3LPu1R2GFW2dL/7R7/EXKTh:zNs-6fEzeD7JZ2]BDcZAp73+%l[ljT2K%}DXA!dX+RWpy}tX&u$-ijQTF/jz[ZkWg!N..n!zB4?BhD+mNDOi4XKSW>0{rLi/Yc@^[*KhzX3)r1AXg#uzsPGoSjb2:iPW#{M#ih5:Jae7V8d*[@nygQi*[PvhIeii?iWM-C9AycAEW%)eLr-:V#Q-TCfSy/pj]y}(TFZtBD4GwV:tQ!u2Un9}07E]}0KVLfsfW2/-uc*MK%c80LA*8i]t0%v-vnxRBl>X5]7CB2OLdFoV6I0{(Jq!)AP!goL9y*GLbTIxvRX&9i0)u/ijzxd6)ej/k:voN)&iB[%6IM$oABkOsb-KmNkNngU^>lvjU/ptsmX?Ny>f?gCP@K*slP?/yX.eoi%fnjv[yc}EM{By--UF7$.[W:/0dL>dI[d44Dnu/1-$Ax/ffM4^{tNOdQ{DV:^NpRnn0t38*uOk5HiW#F$w(=S9tmJ5>jv)YItJk/5SD-m(N.([wcOue4nuGl5H/:j14lBQ&q4iso^{9Me]4GbddBm^5+8qS*s&[lq[+$x/mTL-sUKy#G1qB:IMH8&z#{.!aT!X[(b]4G(73}eNn(^=}+7!u*n:+3[tyqKVh}x{hh(Qdfaxm9qM0)+$Z@DEVa))@iQgXKs&-X}bY3:SOn-L/H*7Y#$>BQGjl:-1z1cc?n3feBs2BUm/CL{OROlaNmGVdC1gOh[&wAiap0xL?G:V%kzwAUO0.:$>}yVj%TE%VaE{12UyT8j7xlaM$:F&Q3E}nRF$jtGXG7I]ciT5u7>xX@Um(1F?KjotTePa!+PpK4#S/a}fHV.9c$.z]=mChMhf^xe>A31f1/sM6Tqm]p![kFKmhEmg4FqQIhn?fPH$!cF@!0sEaIVG=g4()xt?>#13jpQ3O1o8G<:aj!XZU2TwdLRvH$)8K[pC>kpJ=HnK7GxiTm2w$09c&Ue(V4DDaA7-dnpCpzt/W3p^(-B0L2iBZ%@:*3l)j/gt-Xzh]y2.E2qkOVnreSl3-P*ahTb7:8}T8442SkHF6Y8:XIlLhnIv=&M69#Y3-f*mU{(4:2O7qE1U{=ulz9:kh$4kLh0wzlyEeDOko:gwF^+^9E0J(dG475)I9r+7O!.hFr{x+12xo!}!csFQ:wgtO:TdIKdO*0toJMj57oz09yb5vO1d4I2ZL<#3-C@Nd?bN]PwzML1EXGYmKWGeTl3+6mZvXQV>2Uz!c>oxi=2aAAqcl)8XT:dsJ*HyQrycMJ:zjL@Iv)UR6%ef!qTcaoR!D9N!y(/ylFny]P@ckDe+DQJjU1Xh$AsC0JRyJBrFQ-SGDn@@HWt?-#H-1ykX6-!d#LbgZn^Ck{Lo6.i40^bqN+%PQ!&SNB5U$.lfR:/$Ockr0WT4UF#GdbGqo)S!QRmDo4#]-/J@20j@S]LZ#e]g{{y#mCcd*qdMBxlQLpdD]Nj-Xhi-kQRuks^i4kAQq1Z9n&vDaYR6AB?z-S}IYVE[raQHunNo36C^JwC/@olt}ZqRl?cQUdW%jP9(CLu.(0DLt)g9li^R2XN?AC>CF1?A}tiH?vHutIg+zqBN*5//2EwxPT+sW#m2fI0y&G}1s7F!o$A?+TMs&dlSwTN1H3-VSKW-k5c!C/DfzwoPRI9RPWEyn93.kQoM}-)4p8zq/(EQ)HSxYM]DDUX?L+t4=ZwtOgZ8P]tAUQylxsv9aLV}}]MoseA2mOoBhS4r-.+[nf3B?dxG^-bl8M0fLu?XQ=9xUqqJLj0u>p:>?+]gp?)jzy9!Af[kt&Q@k!:CbuQG/r1ObK[)LYd&#Jx}[!V:?2QKy2K@JU4th0q=u9T<0$x*Jmit6Px1p>6B&WD-.*OdA8Mgl@TA4gaF7DJ@xe&[XvLSmJ}D7jceMtl.BK?m{?]6q7@-9W^ufH*gd@?:Xuxarl2OScu$0WPxDH-)b?9MO7z#IhBL-@T(7h*YOVp][D=]uLLpaI1fZL8q%uv7^+6>BLX>3+fpYz1{w=z<=+MPWk=MKNQdQp>j@9@qCDr8IWG+OcbZke6Cj1wZdGi(IULm*wu(Delp*[>$iTUd7pofldG/IRd[s3XyUymMo4og{8GWME[FS.[2Q+[$uBw}!8X/ps1+CK{8BrT2Ai)R)7Gl7vPA>Dg&+E%hHx%hk@cDL/&O!OnYhT{]7v3T-YFO*X*N2ozRadeNAJC[bZK>(WA!ohGL5OaFg&FbWo>o>]YR{[&6ivRh+LwwS7V@NqeJ:FEr$(M&w!Wa)tNdW[Q5S4a=44R!v(J^yXD?G{?UkeP{{JnMx7OT2at/om+EB{Nv]>Gq5an79v-XU@Yt-i4mP^Zo:7f^A-y.Q+!:uo[nA(r!nH%ZQmmrkwm$P**IvTBdFw&[COrB5]cLWzZ<(xs[)^LXP<>wVVGx^)sX/^HydD?3L7Aesl74hd{38&x=4+QT!6Zc%-%3>g4CL?%HT&tI+Fgqtyr>L{7z={Cr/gm8:AG(a]p2Y]0WS=1nke>^HCTkn6A0!cj68b#s(0dXwef3sHKHyLU+k9{AJM8!{YJDCHmMaUd+*jnULVUBE)t-?j+Flg?GVotwFE93S^dq/A7&!q3&a%nBm(vpx+Y}RZ/M7Cbrl4]C.(e-gIt@!)&1U=Sk(XbgLx1#?htXy:{nUD6O(lJl$[Ewi>TfOtwH(zxuIg6F&GvSSyE1oXTy*}B?}[%kH8b!I3bY27(?oTZXf][0GD9}^=4igU(DN#X2kgVZxXg1RqEsF+7-K6(88Cj>>XS6BVfrp/-]35u2{{igPL+$BI)N[V9zH5Kzyq/FOh966Ywshwc$wx-+RB!2rGTLB3H>f<0mEdMoy@Pi[/XD0Bolde}CKrmtYAe:RnHaze]!]9$^hK.vaT>$Vl:df2>O5tqzHA&qw2jswIkDv:-54[YNKm*h$tn#JgwLH9PUH!4Mqk&egr7PV+.g?.Khv?O(8{h(]/Y)u*o&R.aNz(7ai]-B3P/[E1RkVpHseds#A:%&RU[Z{L(zNQ/Za)9^yIb*.9@d*wC4a&lK^hT(uqc#zDPaZ4L[hgc@65HU0u(iu]v64mK46MW{/Y.7^h?LMUv2}x1V2)h[zE5k(-{SiY+&uvtygb+P^9T]d}vxM$t&l(mJ1ebkyKmoDjE*/QnPm2Z2E)GqUeN=eM2cU=<}vhk54Hm2E4a3pH!wI+7ZwJwW}l0l)/@x}/Q/8HbP18/&-t)]Jr}Q39YHwIEUVY{UX=Ia9T]LchVxouEaJ#hPHv%G]EMMT&6Q8:si^uy>jSp7taDr?i0(Tj]a9$]ZDR{$cH/1PBU&13):BiNR$baap@I6&uK&s?zNtg+Qc>F5tnUHP{0Z(=OUVz&3$lVY08W.%3/mnh(mFZ?@hwkqx+Mx5GfbY(3F4a#p$1]gwN>1c(H+V:vR(.=@=0IICpq^+S%t{-uX{rM=I9lj:aIkl+aZEr=i#*28zArMjWcuzeIe8/=@a-GntTo/RxU&VOZ[T:h}LvNb=[7YCEPXCJ?z]!xQN&xatv5tU3IK{mmfc9}:CaLSp4c8d!<-+{dfS8?[1=nF44g)Vg?(X>1G4/NlR{k/>s{0i=LADc-B3HtvBXtDza?.S9QnSy^O}Njpy7^D)L{$yBh%2c}FB3lG=isTi2c{7>:#s1}^$*>66n{l%B2*HTAAknuqTZKxDH6mpCs.x/+rMoKew4/@pacNP8ZyjXkGQym!uB%+J){0SkOF>J?G?pI*%7%TxmZ/cgOu90r1P%<8>HSta/flvZD5Xxzq$]Y&*VPp-C-V3CRa:%&h[pbS1MVJ#N!dvTcS0&:B5=l:YzMF=^5WX!725VUd<>J?s>iQ/:mL}wFhE9{DC&?}O6PJRRl3*u&wofHb!Q0l=gF/PD^?d4cUmb&vJ5}k#x8:3y5i?*jI04GO=Cbax2EZ4*$8fKC$p8Li@M8e1yCmQ>:fwB]E%8%9C^4S#57@v.:@wr7.=2qp*G]6jEqzT3xS![Z=.T0juFOCFrLk.hs?vv/}}dup.K$><.zw:@I:b11J5oGRtUArt-<>r2cI$M*a.UtE^rok.a.6vTpaWJlHEV:y!-2GN(gk7tQxHN=Q2wQXhk*7)GMU-B5Wes@QPhSb/EgK-=V.tNST3oB(2!y>>[j2Z2nd{FAf}2!DlKGkA7cqe[M#=jZ]ia/e:H3yIV8>WP+KX-=-VCJ5>fu6p@yo=oYtcjNf.GEWDQ:zGL-/JEo@I)Aq/i+cyzGirTd6#Js=IUJ3ifaKszjX2w)&zxr^fj-x&(OW5Dcoivtd(PejyoKg.Mh?ge8wjrksOopLpjLCH!zO3D<:FL]m*@)#6$you91UjauU/JcV#b/?&s9pj^3IJ[viRF8qrL6l6F2LnZ61DeTj0agEP18!MEZwVmnCXDl3TcNHJ%hK(m#q2tdZTJ4o*=mHx?=697NN2CZ3Yb+2#up>w1MF4Nq}>VJ2)VQ?5dQ1rCGHi$]2=PQDBB@vBi2!:B3*[hMry!pr]DdLECveT6@=Tad?XstA/r2qz-R+3w=.*=vrXaJAI9yS0c>Z[K=RGk?{U*oHx9$}SO{3SD)DFN2mkgCpG:R30@?BT=DoJ?48WCoxb+cUOoDZ(Jk/05r+o+z5rk+}VK8D2#0+{wr&eUr5$o/aL:^]N%}=*)mN&=BdZbD%3Cl1q6JFgcqN5^QHN}v.4aq41<.JSK]+5H3I(Ce+pi9zvE$A9&^ECW?SA]QIa/:pP:jZtbsZlBtTikQ9Pq[+CFs]t7k?nizSwPpM>rfcsiQmd[.*wG({LAV7hJpyZ)^1iS.%h9GgYMTCSx<-htcJq0i527ky=qsmj]S%SV.X:=*a]Sm8A]=Ssfza/YO5.eeEt43eO4IaPSy$^.?/&0hZL[UbHrnAEbV%V3cfH1g1jfY&BTD1SC@(QtP*B/0NeE@NTicx$p.cIiFAscjI^TdjJ}c.bc?u64S>Ap>oJGtNg^10AT6+7EM.vghr:NAO]p<8SNS?e+n+$f(r)!==Jj?0{Fq6S0mqkuO:G}3SoE:Lt@ohhAf0^dDUc1=.kB9fd=qRrN@AKijb[KBaX8nZ5ZTgXO6-An68Kg/(PnaK7tDwS:lc4G2&0j!:a%5UX(r:muY>!&SQx4.4](M:d[iNdu>th4gxgo{E!t6*b}$nHzJz&H-TkUlz=VO#k&.hY-F&NPZDMt[N]hD$8goLC]mTfs>GWHR!.Qn6=WuJ*D-Rht=mR$ly]M=Bs^V#1hjP@93u+Mob&%Cs%fInW0F<]M6m2Vee3OA:5dRTS4S6hZYTZkV:Y/8.esIHCe1Cai@kyUIZFnZCLv0/zd?q=Eb^]7x/2)mNL5Me*iSJ&4&J^g5Z6%i3u)euWxEECF&!n:zmj.ro+pc[}wwUyia7Gwh[^.?)U/ein<{#s*zDQi}&1YLIQy)+@6Xre3@iJThpKTBL-%yf2QvLE]=9/].!PINH2Q3!gbaHD9UX7@/2Uqx+yxtDZ?xAeHMI5i2LD1O)hSH$aKDa/(X$tqvJWIMca3:nfay^OBK6C&PS7L4gZmx:fS3C77tP>Ic^F74AnXC#x)O@TYbY)MV$N2+YuGUBm[hPIfaID6d{kPq9cm]V]{=)wOo1[L>wA0&=D-@CG%*XK58udwVFzqsUr4$H)^P!H?J={LvV@^cW<40rI2BJ0wFVPOB%U2X{HC=pI[4N(G7zABL-tmP^^NxRc3{55cZiJCqyoRG}&SfTv{H]2HC(BNxr==ou)(A0TE28d!jne*L:0/Yn4o)pnqZ&0&3UQ{ES@I=[XwZ*VbrC=Ei8Pk[/yr5qrcITadiwq)F=[TV{owO7S6rw@t7C7+cmz.xY2M5tS&QMDuCEni&SJtJPuElL!.RQ2Sk.^^t]&edbM.l?HRGrJSFO}e=Y7Jm&dJ3w@1}iY/Pp^gWUbER-.VyK?@xT)A:phK[(w@1fiyWRm3*z+nTY@?(J}wXk7xoAJbII!)]3Q4?<)pbI305JkNHC>myaP(TWaD!?1OiF&AHfsoo8$]wf*g=/7x.D9-?[kZRS<5RfuhfM.ahUWkm(7lTvghLu>^0RBN5>^1ohGY@N!GqMSCC]=P4%38R$D#f0d=+{kCpBopSVkatVra1SDq1*?7uIN/oZ>^]uabTQwtMpvU7+*pIV.65*Zp7oAN#QiIT}:+w0k}@q=k%n&OPLuE/<9tK]N9Ro[Zi@AlUtUQKidQkC]hqiyxGpfjEbVtn.VA47hXPOYGcyt:HCw/ZEB&=A^W8}/CU#xLVBCF68fEIotvKC@&:V5a=7%yj}K8SSozv}@aLdY/e>Xrm!C6>yn5OH16!^Wv1d=Lx=[I!L]S0s65(F=Het]:@rYVFE?1u2i0/)=YAOKjpY1yl/Hv{7SLyv26pQe7A65W@Op)$M(-h^ixe/1c.Ci{uCa:CoBt]hab92AKeUTRh{j4#9sojhc:^/Ymy#b@?hyh/T?m&DK6Nbkv*R#/d7gshU]]5FqDWFsN0->jFJ@rUC7}EeDpkMUm+?n+8/bXS}xB^/e?vZdNgVFM*qrI+Y/k7cEJE!$a&y+Y/#?/z&2hBkM??%u^Nt/9&}]9BdL0KqmVf{iGk<]Na$O+}IoAsroGKf7)mmsy&9pjx>?!EhG8{!x*KRUgtIryUFyF43yvr]x>rDUL(Kp}dbp1:yglI]h!:hRXpYy%S52B>fKM6Cw(RX0+t}>zK481!R-.)-r:XZUow1ywk.ou=o{rNDA^t(55j27H6(A61ccS5TEXFO-yGneFdBOx:h#V{np}V6^U+XFt-nv+#CcOtk%0&tbjgJ.}fw{t5AaW-MCV0B>FaV!IRDKW&zFG6KmarkxOzSP@:uI6-ZE%[$G6#:lsPlbSqysYiqE5=1Pw7+8?SD5FSA/e>{Za9R-+)ry/eoW(HW>oXX39-vg>hh<6)prK}(qHExU3-*Pgoi)4i$e].en[6qbDOpmD^X?5>deGGA/(XO^(=q@P3>JGs[$D5Pr)&=2)4d09SIMSya?WWjH!5dLxZdif{5e4B=D1C:%lbq&CnYy[qs6qCh0X6o)WW4ewPO!*%6vRe^GicBPNL0E.&7*Qy%3M7-A)S5{LNhyp}Ufmd7g^.S!k3mIe^ND<>9m4-IeSfIdJ^>/%P)=x5NmSSXD{1?O[Z-3G:=NnL^1upC^W(8{dRWjVip28D:{DhBDC0vl+ma-#Y>TOhaC3uv)PUhUS+Hp^fdx4kb?hd@mCOR3wl8zJJcxBXW:$eqTC$iap{Q9:4%b.G#7-IcFfcb0m)Z0NL^8kx{//>U6&t2-PV1ysBVC-RaYOU8iW?:%Wk[Gl(H12raw26Ru{t94&?cPda^9HM44HTkIcmyceDggpuaR$!lSzR1S7WQlz:Aw3a-m^T!KA28a=[ie4=xHeD9/>OG6#RDiaGSCr#IjhZVE?3g[3vS^5^Y9373j$mtnX@k*c9THP?FXidLk>AdF&vOW+&l.1py/=Ov?yHpKd[C%@E+YlS8r1k^xew+ztbc(IGB[UiJw8VNn)IxV$.6iiFZZbuS.pCW&}sz-(pELvH[*@OUkvPksJmQO!hIiHTQV7@pevRmsdR1B7dNRE#=rpHK+(.d5xXAO*8ma.?@8Oqxx{>Y8LhHXC^Ta9t1&x8gA$Do[r26!rZJ9$WB*tI8SkWg47ApW]xPILe7LJJ.6aib1VPLN0bjQklB*Y:=rOwI0a2%gF=BAT[#rw2Qe^)1xIYF{wZxQ!G7jVphL}c>[=-oyS(WN$7LSE/2AY^bsq/X[8dZVEY%*HH$^/wBKh2Y1f9]<.]A]0z/Z/d$9i1)i)31@jUss]@A2VRu3qCytn9>^lU9euw:<=c-oKum%:iXDujk^)H{?n156B@)#4YwX[UMso/9Wxhy*T)!}j8P{=@n3XCYLKUQy%@x..%s4sAjp7{gvQS5tj)*KRaKiiOFQoM-llaq?MJ!UW(TU6$Yr1Z(K)1ZfGTDXmu+jk3p-1lp#7l*OX0w#}+1l?DWsZ%KNRvC@g-!KRjMxS!Z)YzEnf2H*Nd!aDtsI14{U/h$8x@9])U)lv*rHOx8dC}*F2!:UGjED&!t*MBDlJY}kJ-*PB5Tsr4n7Z-uz#aqLWfU1$Sop$8bopGER&==C&D}p0%+U+1&Dil]cLym^oo6(&+Y=rT&jIHvZ(B>Wu5*6B/pOU.#fqLdFQ:UQ0b>!7iBHQrV[t42R?51i**Nv5h26fth3wE&gTbZaI&B**hU2?+D=w&CNrzU+FUm$odpnQr?d!7(6BfW*js0U4NVUoL6Sszc*#zJhLQ+Bg?)(r>y46q0=rTfIgA8@=JlTNd61&).ns8>vwJfz7a9U.mWG6ug3-0T.cK6X/>Zs(]orYMZ3dVYi?I?LR9I02SVJWb=2%{m7GX6-oejZF((m*JZRg}X6w1F<1DhgCOtR)HwG-+bhZlmVd1@!uCMqw:$U[C74NMT6rFuXdg^eD.$7Z*FiF8)UA+7jS!gme&N1::I*7Kpg)6Gx8gCTt=%/8(14vgcstOs>F-v*(aoff08[yr-FSI&5#MtXzs:JTEMn**f=#QK:8Lh(=2[W+y:B-&gx=S:eG$-W2rD5NwuN@.Zsj>}PiEZ9YYK}I/%+2%HWC{1GDMMzmjDtwg1IqOvHQ&{6r8B[L5Ok{+nc<6M:%O>qJE:4f]+%6Xf=.7tTD2@Zai^cHU+0)R6nH2RKPd716}FkU*M0a<%GFRd0vwmz88/dp]-b}+)]@iVZQ>K?JAo7m3/.TsIf)5jc%Uh79jUd(UvzSwD@}2uPFh=gy2Geix0l=A$GPauUw>H!*PbfKl}eAVKY{#FFSbECN-IC(?[hU/Ya3hx5>hO63-E!cIoXODvpmg%[2Ko.@kqPFiZR=i0NFT[4<9k&Hk-O.mH9aveqV/#/Fmf6{mi@?Ggk![mh1u:JLIwg3D3nt[Ic^-c2KMQZoAU0njKpkIckU)E:UZ!UjFeB}nDwQ?8}mNmeDYP1J6h&i[6P(f[fH+Va=e&oni?N(rDK&WyNNw@tDSdzU3J&Cv>G(B]fA%VJj{f9Ch@BW(0TAY!ihmYrKGvOe2mhe@l=RI8]B27b:/O2&EP1WI3Yiws&bST=KB-(^Ga81rrnY!cbMNT}2n.:FYd+v{wj{r@i.*iWA6NBBd(]#-ZEoyV1T%jofh/A7lUfUNC]C9!xfwhgaN)Oi=%)M8Nt^-E6+&&IR*crXB??w[(%Yp}rA]tuF#QIP?[Pr91KKiiVRXDMPDKo51AG4&Pn+)gEi.f%zp-AoHD+)ge6UW$A57cwfs/gXQsp/&PWK1@.gJCN.KU$.G?EWO?&J+Y+ij?bwpg6PZ1OGv0CbVcaE03RkER1$]RIvp1}(a3E3+5o:a!&]SuSXFA=%J=1#MV8UtSk%e4j!$kW/jqF5zXD2)SknAxy^k)?Z9mcx=I*h}Ou?xttJ6GQ=Lmnq/yUB?gZhNZxPaH:c#0M7v2iMq6u&fHuYbq-Mt4MWOTwVbnpjHux]20lNjzk4g(qVTkAD65r#&Cs6pX7=qtH62LqCt=X/VMy7U13Wz72e=/-K9v$3jgejX$9U!-JSSWRj)wx*(4FSo+J3hkSqvkk(Xu=D6.F*2[5CQ%tM--oE/4hhG=J%#AmD+t[:f5tS9iflT=i)MmFksJA^1w[65>X!ntq1QI9G#u(x/iyiP(=+oZgr}*d^U^}%Uog7.#*gehvDIe3MWa1mR480qP0xqni-W8PQ}5Ns^kImh^o}5p]wRHL4$F*%B-q90?G!21R1Bcp5G5dx1yN9!*$UYrEo/oTchIG4=K{=%&q)/T-/z3c*%gOjwgwp9&5)xP9@T}ppApo.YXYn%{h}qZ2s/3g#fv&$jkR?acHtQ(RK{uiT]c1GRC>>>JDrM=+*$F[EBid{qfLd16LkK]x4ZM-FoK6Rb-{te-8mXF.my5oX]J@@H)De2>:$-:-F!JHYM=FMI&gZ0jyB]?&]V/VJ*.yuTINJQ[=A?wlq<6LAV}dEVnN:42iPeN@RfdS#w#Y-*fr*CBRVtMXc!nc1]@ALm)ei:fIvaik-im:WwF4=2rcl-Ks%q>l2rilGsG}Kou$2{HkwT=qO{YZ:LT1>S@zc4QrS0)]761Mek#jIorN)-$gOw>&![?gIb[:aB&XrLzgV.9BP:YO9wWDXX5HI@aKw8OnYC1zmmZ#8Bq=i.(T$#pne9OniFHmZdvm(N8>?)iMhdmMtE8Vcg*{:*hiMv.9@UOiMjTM8Du^2BTs<}VHO}bWanPDl&IE[t8okdS)97.Ym5qSqTGX0h7?t4oCv7xk](-Q@pyaXDBq>Cx!QuXzlLvdLnGw}oHI]mw>u]Q3Ox>S}}2@{ic9Zmceby{9qA!(Xb+FScaf]#$&CB6mL^.kJ:t.KvX^tkK*kE7r*OWd2&@)=&>{ZYQY[>Jo[1pirXPwNmIMS>X^%72h]LspV@Gzb)DUr<.PlimV19]6qF!DIOSf#xo]7IQhC)WuJ6Azx/fq8QiwXBYADH)G:>$1ZKys:-S#nuzrMM:{HjuAFDYYHqi4mj{KLml4edaDc9{+rg9eIeC2RNzJa]E>OB8:}FjRk@1v:b+J&jJNwJGVJ!!ME<+K1shEFb!?q^.6fJ@wti!3mD[AF.A]zGkz&KVkmL01)+tFBVLwX79ergc:Q#zFL*Kd7kj&jcsD@*Te.n!!Pmw3PF$lf%z6:aqll.0]etq=-5(br=m6.}=KuuAjyrlOGNy0Q2v)+Q>Zh#sPm@L6H>Zo$0k1PaVajk2>Pl8DuJ$6A%/pKBB.7RTXw-&j1+Jf=ahq!%J&V&{wPZZk3hJrpn)+CXA{sfK{]dQLPmG@/i@#LKNMdN/Rk#?O^tLZ2^x0SvI.f{oOc}F#.[e3%8.+eL)Ei4Y=>EHnu2KaORi-cr?^<&zggVE=ec50P@m8{ynmvSJ3=%)}I=n4Z!v/2zqLpaZVse40D$6/0w/nQJB*[^Q9s!bhmR+hmjA).M!=dSsz>p)qYqm-NkWJ32yAqN6e97vKU:neI9YJ$wQ=y+06r&C%@-dyWHr[}}/wu1GW/L)c-Zl$Jy-S!0jiutubKUeWCPyk#F)CbYMFNETuH(yi@uj*0:9rgy3x^@]IdWd^:=Mz25Ae9Y>4uIW=LZp(($S9!eG>>qPG9&ZQK}t9tifSD7eJx$N2w214$Xm@Pk0/ZgtGyNL8w^bJP*1L!5JY4Yxji+F3dF5C):Af9r+Xe/&@biGOzl6c-Z-41kV@Qp#T]l*9(X]#7DtIm=CpQQn9VaH/mEGS/t]#btC(RHOZ8vV(PnG#/&ZI]O[3WXxush8:8X*]XOV]+5EAoU-03/tGp8@rF08rc]^4J:E:27X/eTor5@t!a>vl}eH6s:ZZ5Mc))32CFU^LXr!HjdP}x&ljq&7CrA9BYwDyv^M]*{vCFj/K$i.d[f[iUGhEKnb+1x)IgeS.R%5XPx@HB4]QNKO*4=t&)XHHp*ve?ppKz>n2q5IgFLhH5gQxl>Fhi9vQ2M^bu%9berlqz9KlgI]JNK7qz*#f$f0Vxdw]p9M7?W3cfJG?)d$IEb6x$W7T/eraF]1LLfc6q*m5nPPE(u=&F6aZojv.!:eIO87DJ.N1M>pAM3EZulVy2]IwdvZewonyPv!g3qwK2nOVglum]>GpeDP>D&(U&:RC&:}^Yi9YgX/!rS)*@^#O@XYm?$E(X>UaLerTS>WYL-Hld5Qgj+DDA4CCxT}zH//E.W1RANPmT/Z[m(&0N*-G.u]BOSiN:&((a<[9z-d?9.!KEOQi-hK/wtwrUmakGP{+W3E>&O+M9ZWglA!iv).kqSu@H?:*ts&hqVa7>G+Xa.cz}Sig-z&I[38Lvvv+F4>/sYj]Z}gJCFO0(y*IpHrPfzvwdQyJ0D!3P%8VaznP!W]ji-c+U^NG/*vIpbffC2DQaC5<9lN8&}6WmxXT*$ni%4KnM7{I:UQRs:]CQ8r6oNQtl$iLiyMKj8D>fI6t83Y>h1{zu:=LIk6w&(}MpFAChxm08-bg:0ATAD!$%MkbvgZY#OiPRdQMJU7pQb+@jxL78#?k)%GY@3K.*s4&wX[p[UKlc!iv!oB3/ffvGNqhg4[Q9kPt}}rl&-pE]]MYo:}b]QlM1dgtI1CS4W:)k@!Pn98H<]7Q*83WUWpUS@57c@jAF>0IUsW0!WV^E>f6&ayVC>HLR)E{9All#QFt1p1{P$7k19lZSfEoIz@lTZ}Xij5o{4K[17!*He3NSEp3>H#C5C?P&q!C@%(*C1g566+tV-BrL)%mU[x5)cFS]dD0-RZp!0Su*N0):c@]K+mN2^19x}(#l)!x@uQnq7*nVsxOWw.n{*xq8ahI(HrziMVtX$ni)}*KxF}w}=Y)TB^?J2?oS4v}zS4ISt.HbOvEazo{DtXt{gX26z1iD9@CWm(jhksnyvqlN}!L9G)D8H9ATthB4kPQ1SjL!^VwSC:zxLXPx*i)OFwm5d:.tNjG-R)8+gEuBVCvIO!(STKUD>B[}u(9SyPBEHKO&X)A0EHlABe=Y}7k5i>.OezM@xu>g{QTdH2m-8bX+=nUmFhB+8&#+VOGaOj0MC2{kqmRYjWtr(kOtFhDmfD+v*rELVv9iozCvLzFgTGM9N?JLUkX+cVry6D{JLn:ZLy0sWF6?4-a7w1}5:X(jA#}q.]@LAZPsWs^@Cz)=kQ/=!:ZmeEkRP=-DCoDbpAM0.7z4.]kSP(F%OclX%=k/4OZT/9*sJ6#c+!c8cC5Kt8%JlX.wPIi+yQks%mH(I@oja!xyDmNwF[OgKHP6fQ}jXvw{V$I?cN:!kYb})-35D6PI$lq-K9BPHY1P@L]hk]N(v)F7R0gizbdj44!mzrdHN!N:Ns8r+Y[X2YV60Barvib^JLfjMQW!C26EFsVIIGS6SDo+Q}UR1oSw+whsQuVm.gstdvjsv4bCJ-FHS10=CE=fxJ5PqTSj{OhuR=ecy{b$@CD0g-P]/9YyY:jd$.yk-Dc>d:{Q[+=5Q}@*FQbalCEAErm9iC1jV*0BAgc@cQYgS4%91WS/+FzoFXN?z7r@&9+iz}>Vfn2MI>Kw&pYiMdcH3vmJ-CGM{m(B>7Efw#5kUihn8m(qc5Ici]ge<}mrs:e0BRSn8a4:/uI2Y4=BA-q^*47?L>rjxz]=@eH6EcYgQC:f/{V1Iy^.I(Ycn]kLljbG]k)*tR%&@IDV]BYz[BP6QEa0&qA(uzDB6gj[A1GY$PiM8I&3?qd>6RvP[5s<>mtnqiaLJ+RI]+c:Cl#3CrVeFlK3Izoqt(1=W4F1#9yNJI..m)}VtI)%*daUV8c>%)+]VuZUW7>GZowhN*T$lkj2>/jCa0z)ZIIX}%7}OKi.I7HUIZZvkA7/J}c7A-(:v[-un[#wEmAM%7K{SbE=ErI^K!XjzqQF*gV!TxYhNhbgXQg>XbTmWlX^F7Vu76T[^w#Z[$Lcb^Z5C3:%$w)QSny!ic><:>m=Z})@}rETxX9@g6}1TaBM-]qX%m(AQ{N/k2NWNC:*Ua.IYsJunL}6gh(ZgMc2hC/AmQP#{6SO{d($F})UD?mj0i&GWu+Vu@^f(e53jW=$9&}6MY?AZuQHeVxo+k[@CT2hh.&Z#gn8Dz1M%DP4.G?(O1N!D6z^mY[Y]j!%6I!OTOz#%#K2-sJ1E?^-:QK5Ma^p$NhTSt@hTKsc!S-1*DpuP1EU.j/O{hVPH.)/@&l)Sy0UYN]qu}X!S7l-O9UimCK2Ec7K^=*7/U0w@e390w1yc2Dc&./*%5o5yRdpa=pDs&&.R?N2&SJn=5K)WIV[XfUa!=+VG#Uy%o-BXf/XOQFo@JOH2$Nuaw86OtZ}REE{BD$HsjmZr2qfdtux-aFRWAHZEDW^lCN%iE@z[ZH16sQDt3uK7Ef]#xOuVM[!#.H%ftEO$n0=HQ(5E$zZvEBVWejpev.v$E]9XvauE^{U=bJgMJwC4IS}Ckv*C?xMF&x{z=P&5tPHjG-FraR.]G>m<%k2:>p:#wgSO^=as{s1=az$X(dj#p8CA*zZ{!2BG2oUXmGUyl6r}26L#J0G&Y.Rk{:6iJr$y=vljOfw+4G-OZ4gv&klH+1R$}!aR1{&{^v21A1AEt/]J@/G#]/uy]cdw44NUgjoyY%qt7ss(g!}@kcdU*P!{f0YQSB5gAv]T?Nrm0{TBhHH/v&9C(NUg)-L{bnH$jm(c*&(C*LZWY^*4e)ieVcd%XQ8y0f*H)tTIPcK]&.:P2kvJ+2@Te#!IDhF75Vb#70KS=5qe#q=(?uoXusYhsLM=ziRVFwH*HT(8ABrt07xkek@-6*gj2w?5V8bv}hYImi56e>z@Wa7dT+o{VLau8^DT{MZA)LrFmAO>^R4tKhxTmGY[)0WOE6eg:djKotgNx)lCFPHY70f/K<]y7.-Tg/XT4v:uO4QGAC1g3B4S?.V2O3R&8GlXZH.tm{42wgp{krA[pQR%wh*GijX[#NqyTdP7xAHS[[a)HI0+J5i9aoqDx', - 219247, + 'u?rJC0oIk*w@E/Ax1wCB7FH-q}Sxd%-a7>Sj4y*y[WgaxnQQV@ztbq@X2Jn3[pmGmrAi3Rhk#Lt4p}:7F#icX6{K0rLUj:aXu-wM(1B-Yg?ZuL3jVKF&U6>Wdq{WanbSm7O@RI@i}k3Zu2Vr}[uOQX3wVDWau^m7.uzn&34@c{P^/&&g:P(n.e0ha/ej#}ENG+.(w[pC^-VfzvKUryd=aOfCJ955%&.!Kz<=GtL9{g(c=yz[Cc$MD#43x:JYrHb#EE0H6LmWz-4yY-O.*B-Yc/(J?31zmsjAir66+)NN=tXhT.Wb6pLXwJQn@QDKzKE]/UVk5>^5C-3Gqv[?K!0mOo%ZKPcN5MLN2?L1pU{:36c7kOd5R8b50(YQn3yWty}-)Amp1n0Z92*.O=>WRGy/:cNcWZ4faCw@ElDLp/N-CWBxpKE&qZxgQYT#K!0d%peZ#n=Lt]V@2?zrw@gA@I[D*^W&W]*X(H^m&I@]dd58ZC0U6-8?HxnS4a#)y5ysewYB7K8])V+VV$F>0@=PkTfTjG@IrrolOQF=!HG5v!=Fl2l:Yesy)9*qFPre[f?L#KG@lieQ8^s^TjiWM:=aUvdpmQ^XYXDlL.za/6{qwo&T41:qO%nSbH*1$]Ue1I3fJ=X/s*C^W)k5%#LV:T-9Sr>gpgHG+7Ki^JnujUw.z^s-hQ9MQHIL]GGrs(p!NQOK0?puXULs@}FRL(]:5.9Kia#&2w0yoC05*X>&-UJ->ryhprl)2e3@.jU.zKLE/<[vd4{ftw^#Z8gK+6[Bp]Z3(Rr8qA(3!A7GlpnE=sqk/a#eLMmRugUsy&{XAB<}>FLnDNSl#z>ikLC7{cLZho?iBz)7pZ#atzE(!6/9/L$0MFjw[$d=Nrd$GCp383w}erm}*&<<}&xCEWgo4eAr62j}!U$%^K+zv0IYL>06%0g>ir0:pd=&FrLTCsz$(16[@3p{fCaR^BbiXAFY.]GJa)n[]OY(4Ya/S6>m0XYImu)KJ$nY6Ohw4)ROEIJO>60d6E^(L<.lMA>X!MM1.1jQ:GnIoJ?D.A$6nf/:$Utlm7PgmHz(dVF[CwkL1}U]@:qU$xV)!b(-KPm]P0!rXA1%WpzoV4$mEpKV}qf&]Qi4YGYj(S5O<09{H.f2B)b?$J}wjf$/f/5!BB0d7PKC.9ZdBgXzn}rcm0qi]*MFKz*rNlR4pO)tNaGfP[LXPoWVtMT/Ok%GN!Nd#aUFo:Bcf%8ryU!k$xv!YYq5j]vGwn@Fc]sv>PUP(Xn$&)zEb.?HH%Z30pQp-F2X}O6*[#Nk^zry5<2=*x]lheZrfEeHQP*vB@Ue?/O#@/9KXxllZ]FD:MBqPERMiB0SJqrBlKL?He^{mnaqMNk6hmn>@a!B^UOE6j$1-o@)x9^Spq#J2AT#%gk5O!:z^F:pUpD{OA*NHdmn}>@xqGddGK1ODqI0VBnqv3qoQRFD#yCj:(OXcv:Ge(t5sG6oS[wP.u9xuzZu{AboG6jmmqIM&+hqq{si>W0hn1f$2>AN+Wh%yo}ekp.nZu&7w315hgb])j+js!K^>FmGeu5TV*}QXZgoHBx{:xot.kv3jsYspPOpgx}}}c!9MH-CP+^[F}gZ3.-usUBqP8b}S=XxX(9JykZej(3v+m%z&lP)8W.VME7oKb<-c+dn&Y7iohBObdl+v2>Fjir1dZj?VhFp*T5QrM.p4(!<%?j)ca]Vho$2T1<8}efM/c8SGXz.@BEAIF6xJ>FrBMWR*JKb8VC#)c)3wN?4Ddm<%gpfXdzW!HaZ&5B1R4NbPkm5P&*9(brZLk9aCE+^s3t{Z)-8fP8{3OA%8msRCM{RA*9waP0cimS6c/JOQb%2K8uHMnzA[iI{kt*{wb[2GZ]jSl9fq6Ym(KrCQ>Grw7[29mqueD}nM7OZ:4hTFwNLc1boPBly^sk!ghIGyIZnTtbhji3SmtqL{FvW8$29o0e^>ZU(}dUr:vZDI{yYL?m=Zo+u=#ZCN16Pt&n]>D?svTnB@vkK4JF@!FU(%cLcJ]>(st/$RN?n2TF(FaXNYx]IQ&T^.7UpQ]&sbRV1d8r)gf^m^bU%he-2}$fm9%g:c1&s&Z8YN1qZUFw%MO2Ep{yR*harz&TZ[l&9-EgDZ(ZMlbEQZ)a2F$Ppu^{3=!n1&5yT@far-!@#VeeZAPz#}F/YUM?R:BD&oCD:g)o2Ax=Hu(qeHv?3{whCka!5r3Q=D@PU-w./2z:V6UmcZW7=Nf2[l1GvWsxM2O92f^R0}%lz/uZX-w)=U{b^D5{?g(PjV)hWMb9rOB8I?sX!xkjBaPDy}v?hR&tozT2L=Cw6H/{!Pxeo[&bd9*4#%dj3L8:SIM.(T8BUzxLcV%A:@J?zr]oF6bJpSs]9gc.j@xYP1Pyy&Ntny{53a:sZPI(!O]ri@l8i^]^9ukbsrNC)&d.{5L9c^2@9F2u!(XD6OgEer4as!!=J.%*nY[Z2@o5*jC9Uta@/yku(9r(HK4v?d/9#-kuF:aWclnbhck>fMIdI[0+)bVPn(dq3{%X}m8Leql(QM-en#My/AbYN9K^Fe-r}:92zGKUvv^EJc@=78F2+9N0ImZS.VJP*G-j0Z9?2XTw]wA:o]eUETJZB@0dzz}FOoZGvo]lzc1ya9=U<[8z7v5c#xjfd0nWJjKDOKi.0&UpM!e[*oHtNc9Z#^1fM-?+n!CELvz4qTNN3]q[8Pv]48ERBd5nu=0}BiF$OfFw<&v0(?c?&YP@$y7P{oo!#r}A}&PfdI*q^T&4O)AoBM@Xpro(!0Px!OsoEOb-tCojp.34@mO^u1nW:v(G$gyDTy.%>*oveU#+Y<-TqpYEXovtf+{(J$dH9(Jf^Hv!9=2X7k5J*HYp>]nT3iG7/S/E$k^apuq[1g{SUz!d/@1xliX.=naf-9TN7.:i.&bzHd.3M7HdH&R(]ApTUYdFmhJhn.B$=@giBHz.YU1em.q7NkQ0&&bI?W5T9+W@O=#=Z6k-v=po8p$sJ]EZ+Lp6-(eC*)F3vl56uG3o}iPrSBIwr-C^=wg*U]C1L4OYqTtKJ{QqV{S].{]%SL4u2$6@]ljz7l&2.y}m#.:xyiq:X)1M?mD3h2l8MN3NK19&CQ4Mbuw?Dugwbx&3RaCM6S>bN1kUXj5{?X(tbWKjUtG!A+:$aT@wib?#)yoZ)x*U8aYiWn65A+jm3-^?=o>3DN39w#ijCt6z7xwu[76e%St5lu]y0j3^b1sLK[w%K@on}+Fr$tS/z/6&.{XOC&w{S8jeD!5hsyLP<1}EpW[x.bGh+*/^6LOB!)yl/PStbfPIS3z>{dVESL$=BwYr+[qn)9}UpoI4:v^O]a7+t6Y

>g43++:Xnlu.x6N$)Z%ueL$Ar>oT3.%9huR47>eeb^QG6E&2q:j{A+KUfI9(.+fBel{KGyVS8D8GcXm-+zg!q$hWw[9LwD$#TRZGlhKel(vVWD.-8zGRwG}R6/)#OexB/gz1C-hHMz8(!opP^G(h7r]kO/JHZYKR3bCnthHU{eZ(-]2H?.HNi0BoZ#-ekFG$9?VY8=Lo!V++3?^LAxwdW^2e7mNbtfincH)1Y85ao>8pP{2]cn}a7^P!)PWLPO0bI/gz1[WC**k6tM0TiI^Ewb-z5?]>cY(MO.(rM@j3!-5PV%1ti7@5T9Zo^K<6U6!bsih?!/[3&UNT40g1B-n[eHKZ[(Wq=}KIu^fT2-MQifOb%AEyS[{tIAg?Ni*r)/AMp[T]dk^<677TXh/R6hV@3ORTb$/1]GP)piC2AHldco.)7/3<[UxN$:wLByz+)(xRT@1YMUFHQrRvlFPd9v[!YwPQ6mOND8V]dv4S^]WJ(iTt}-lwDJBx/v8BTH@&W9]o4irW7+C]5d@]h!GKy=Y:Y{NNXeCLph7l1?HLOIM}>rdUy@RD0!+XaxIWHeLCRWZnzwM&1dJA:jE@F^xdUqz^Hf65!=?GEjNMFl=w7fT09W>DN544]FS!ABtCu*J^#o7r0H*p&W#S^2O73NX]gYO&0)PV[HE3}]4y}=?#SAtjM&Tx$w@P>[ba{dgT+cepn9R?5ybSzkuA:t1Q%t]VV2Q8^VeT-+eGgSQSr=%i3@ntR*qQI#?-:F%ju=w=r]JYk9M5Hp?$Sm=5{ow&!iNP&mH}I(Y}EbKh538glttY.lt)zaW)8)Dot(!P2yIioz^^73=#$D/?2K>W-*&@Fu.vzmk:V/u%f@J@7BEvD6HAw)qFWfQ6Tca2KbJQsi>(+u25h{RN[>z1EOfh]rIlp2U3w25wcDbP8WjQRRq:4.$Nx)mFnECsOK$wPYPS+u@6)*-[-pU5)[gyOG0)/3c2W$mNW:Y>bmTQmWSBoh2*z^g[Y2nP:!lwiIqZm2.kjj4lY.(N^.zE^*aZzP*1qjz^H1sl2iXt{4fE-QpH:Q48x7+.82=:3jMy%RGt0d!vTwujovCh&M}OK{:]![d-a@)E*9pL7zzMtA7q}$BL3/qt6D4j99g9rg(}6UQDq[{5G0cmJ[N*Z1=KK4gq-OMtG/4Dg9!wh.5+4lsly7Sl1Gcnb32TBmF:kJO5{OT3oItT:&j.eod6w92&ZR]Rs174kgfHxibGTOa18MN^C/ewaifhaY:C//CI/yp6V2157L.i-s4v%5jRm4UPu^Pa??H<%%?c*?yO&pTSh9?Y3OG3:RsVK4>?eX5*Or4!j%D&LV:ma(${any3mlF*(00E8=AX.V(YTYk}w@Oc[yU8=gk&l{V%]eX*Fef+lCk=8zRPgo+BF>F(ab00lYm5@2p@hN!S@7oIrTdtIe?mC#F?WzQg(STqi^N42$}3n@[YlFWAaI8Q!x/pvmW[fiq2^)-3uvq(JsAKYL>Mqvt3I[ci1hQ1hFc{7uTiB{V(z[?R@joN*v&F^.vB&08+%3iAm7Wp]C+nRXK8?dzWg8^1YAbVg@h^BY[9+A{09]/uT3%0#-T=NM}?Rdz5O$Yjg^?e3wD{<:xQv!!rGm*F{Br+mXz%?Yhgi>=oGg7/pP6fBOo1p}d/U[vJTCc2v@b]L7eZTNhVG2u90O>u0Y}XK*6F[3=BeOLTK@o+17B.]?e8ov+L:b-rjunDJI+Q*^Zcxk+)k9uqIvv+Jju-:V!R(.)TkcVnW0wugvoR^/go=V7gJ9L{%!5j+Gz:[/k9]AWEE)c}&AG:a?XkGSr(Mwc3X@(ZqWFco#td&(7g-txnr9KCoDSLEvjy$q:Rb&<>J]hU1M<2i)(&#AAs].$s0+!S1gQeVij7<:TXvdp!VMu.{Kz!Z-ZF0hS(/1BJ(Mr%iTCQ7Lecwt^/VM{T!/JDPZI8?IKP^RSN+cHd52$3s2uwY}CBA^3eb=e1WK3zC7h!nejiVq#M9v?)kq]8N-0+@%ga<9!2k7Eh0p-Ymh4&1bn0BLpXKc8uR}-w{P(Lja5wKgiM(Wq*Y[>*WQMpMr4r?F}-ZwTJDk%(MscaUA4o-0o7J)z}[Dg0bWC6kJUZ0zCxR$tpd&b($IpAwXg6}pj2*#pAaVo5r@t{nGD+@B8q3i>&9kL)Y(8rMoafzI:^lPg*xmMsw?/01>vyo^wQ]yTsS0e5=rQEIjJ)]?C0X%S24VKj+X#I+Wsnn^Be2a82AP+F1:}8yLK$zUW5FO1>j6raQOu(TjS^=N)Ip!?qu7biOTxP(x=k!1h]!gsMp=aELPp>e9tBpJsQa2m]xtfhg]g$Eq)+?GmV#F]^d[DkH5T40{zc8vk!PNW2U[:T#cH0FQ/F:2V{%^zK:VIj/--n&?jALVPPro@JpG$>!O3P*T5#%h:{LeOBCcQ.H%doNA}+aS%ntla]NY{!HtD{Jr.M3usYpxvN9b-8IZgvsD)Tkn$nt:Trh@K:c(A-1U)!P4i2]W(^%1!>xPf2c(aZ>[G1)!i/EhxUHj8yhgg*U46&j#rFj?u[vtao[wP/u-j*dG?P?-p$GEv[]EH-O@WE5b>/cB>db8pD]BBPXDEUxrnqsHVm&!zvi4%0GUvTYOEWFlLxmvm=4aK]S9L-hP%?A/Ub)jQx:+DPM1yN8(uxG${tlfs.a{qwO^{P5ol]3Lt>hcNYO9esklV(1)W>8wJ8Qm}FOMrK>2HA+-S#-!>Rb.K#lGQBtLa!Wq}KH2HvDPZoT^Bvpt=&Df6sTqN.01ArqmotjlMUQN*krTcs2JV3A1zKtS+5xi0=NnHL!lb]ULevLNxv+=RItU+Pl@X.0d-c6V7XN7L/[ob4f]XC9n?YYn5NcC*Og{<5YK^P+!*D)(!u->I4KmZbKRak6{BjOhXvZ!@iOiFf{M6Bcgj*0a.:eQLTfweIjR8ZrfZ55t)8U:2bL$+xzfd}/(eD&:+xOD(5J+9rI&7h(uQ0YO?f45nCaVxLb1Y+K[kqhp+uaBQQbTD^j4&F&bBaC1@Rxy18/z(:&tghh*SMV=cSmj5Ajm@>/^tstCej3=f791xi%n5uK5uK?vlv*=CV+-bh+2w}caFPZ[dXH.20@2%a)#K14qlby23>HrAO:d@e9zz]*qZO}Z{mXK5r$e*rOy6[*0^8ORz7:)ep]DV.-i=X.Fn+z}#aVSVzsLCJB5cyC7.N9GP#lne{ha@9+g?jk)wn[2%/fD+J$Z[8ZZo0DlSqGiMn*).Bb3:rg]k]@HZ1Yh0r2Rqq2qQHVc/dp3}n*dQ-++Say@{Uw]fC&6mzE[9)yfT5.A]$td6]3BKl+EjsRFyMAn9cOw41JDtxyA3DP48B!HCv-k9m6xh}D+MUlR!9X{2hRXWpU]/dIj=b5]L.BP5{$?-*PIl.r[F]#xI{wNfxajr/[o=:AreE1{HMiia1.]8Aw5rfS!:j4o6F)*@7#fUT(PkWTw7X0deNYG/MrO%6z5>SXsBkn5z:tg1J-/6P=3yU=Y@gSQ)ZVjIR131FF.[e]5+ze@Q+5cr8VK>lzfx)i+jBkN-?pVP.TPlbpf@Oa.v{>nFX>>Cp{u2=x-E*SztuzxgRHNrOB*+2Sh3Y}$NuWGm57:<0}zplknF.n!:lOsH}1ZU>iQgV+[im:7@s2E7=+k^<5m^!8aCJ7Ls[Rm!F9]JBe8G(60d&FH=&Sl&B5RzN:Sy})MsF.gkb]&5:SlzAH-/yXNeu2EzkiR&.huuH+OQWEb&Dr07U7N$!:S}F=BVvi5c:{i%Or5iea!u1plZnQ5>HJMzbw#Q4+j>rJ+S)Al8?Aq.ZcG9pGu/nWmtrSp#gM)>:j-p6WUH@Mp605K{&E?N]*y!>q?n$uaD#1XiVHe=T]#n%n5EH:aG?=lQ!$uP{7E-y+C4%(7EFWoBcst#es5+lbFU}94i&HGC?khh(Ay[M]ux$:2y*#v?>+S-aD)Rt0Ww6{[:O$89f=aq+.Zj2[Am+-5s7:%v99+Dvo@I!YuCYCa9e?zn*?:5K5spaoeBo!k8{%W!2%31?e$O]3JB]CZq(hE&OJNBVdK>aA(g=:KsN[k}JP^#}8Tb33i]RASGh7B.WO*O]CfLLlh3-#(P%f%*B!1*WeK)iK6Ex4gIX^i&35{MNy(-z{WO&{{Wx:?(b>?XAhd!#l&nT38[^-19=[*B(A!9#iy&I4wWSig?mE]Y4l0Q+fdGCN>U(crPHOh^InejpjQR(2P2290-6#CD@!I3UK^^gRu(Wh>dw7TS($bb>I?Cs{iu>IKx(VpGT@47zR3iy{RbCNJT)LXtN}E6/5{}wr5CWV+vqf?#j=cjgr94ePKD.wsl7hcaMHd)P^Z^TLj^ltbrZf^52vF2jm]%LNKS.ixs4$#@p*Etf)*}0^[17H@U&BB7#/JK&NUI::&yR=ppL-.oc)uBEYXWQ@=uKf23G?)+SE[?)2{iPUHNRKX$.lASTD%x/+NncsP&NHhlw(aSagqvt$GMUbsqWV(?a&RJ6:WYffT^G4!x#!5uIm54oOZ4=rU<=9RJ0wa10ME&7nIXP*tDOcxV83roN[b9&CrcpJRxUPFg]*QFVn30kPLCfvUR-PpMb]LP(GR7Jms1Y%i+5C:z%-KR&rwsN0z2n2JKvv+:Ly0L[H$ocxPdXkb$cx02YSx8uW4IwwuQ^Zo#LhSc)kI{[Uk2>tEj=5-o0vfa-Y-vJt3@f2wrvm/D>WqQbCNNqB.}vKPHeW102+9/}1XXIYi/.8L(]ixOI(=KJXBbn{8M=nS%=Xcv^^00JvaUx+mSUbp+!Zm30ii)fVkCIJjldL/+jCTAuqGBX1](I*QJ:+XA%kyYkI$@1:%k:u9pHe2#B]]L5/S*fvjNs18vw?@TQscP6EF&Ks$2/BLw4VNs@&:DM?b5vPf#X%295%ne4*arYe8UF5Z[lz&^i.ElO{&H.(wGFxPt#Bm93tWhM(xK7@EHFVkj=G!=4)kB$33p2!1Z}qh#/!5@Y4[(oWtieTNRw!epovvK>^/VGYIgtbl5wAwD2dwO7o1#O%G{@Y57VYL@+y]*S4Lw^#bJTqQa]6{:HM4RMB{$sR4Ef[2L56gWXI[f(U:ZcTEvE8OkvpUnjlJYL8wAW:8r%&S]++HWEfNWusWV^3-]v:XXoxs/RMwMJxt1A5!@fv)nF]=lEJPgYYRos[&p02IBlKw>LOGuB2SH(wMth%OPJ>Wx46y6!+)tPCn/@]T1cx3Vjd%RroF>&a(Ft:1c?[9./ZhyQ:(enJ9IpjA!+Qu<[4K8[L0I&g.?zpy-pw$+w)4@T[P$FS+*ZO46x$}inPkf}il$d]o)8oDi47.QAHR2@F*UQf3MAHB+2*DCGs%Ff>db71jzO^XkIS#BM^fK?l-iNI}}UrB.+YH+o0@^DgJZ)n)pM65WX#JY1g7e5OY#uhv+Ni=*f][R]?S}+q!bJnq0OFcO8)!RP4K%:j*GmgW5oQDEoX@(8AXHQ}w!F7/sC5HTZ.]jExDP]ov?biI7bVWh!!uMt.MS:=:pRNE}>1W.ZyyqQ4]OGVo{HJy)62%zG(&G^0cmWy]D1(=4zv2Dxy[4uTBfVP3/BW&/%1r::txo1z#]<[8^6&7s^ZAlJ(nhH3V{9]-qqcXvt.BCr.2K6[vC.FO^Pzy}g0pv7.ECAx{nz:xNF{prWI&A>M8^7]^:4+1bbZZT$1Jo)5ncm#/VW:#[oLw6J-i.}/kC)R1Wz70Uf{J38>Rm+Vki6AU)9US=^O%[qrC:F8]S=^j.admN}>FTVCY6j$P79Nd)tu:VszH#vO{sry/7<9=[SG9ZIZ4-Y+yTK0=sV0!O6CGb@IH@wu*rbA6HQhh7f?vXF8aY5&MD&2gHyjP&tNYWG-hbnxpw$pN$-Arrz]il:B=sw&7/qIh@sa)}}tGfCOY:]EHzatt4or1WJ/z3cUmCd&jp?yzps=FMxH%qkOtiM2J?X./JPTJvZ&hLNHPx{eZ!mX!2Ynu8lbltb5)*}if?twPfZDkE>qT^dE4sd$bGlPJcc*.Khwq73QM5F4IcD&Rw3Co#.ElVDZ+&SH.phbh35Mif^@&[G[H8YP!&xrx1+P?>S})y87YlHtRMZGdA=z*1yk++.ZE=lf4HxQxGAr2vAsf6kmZ2/$I{%zTF<*SaE{@N&Ps4XboBXxXn[5W{^xZn/0RrN[]NWBOTdL3m.E}]ocwU:Dc*ON&UJnif?U(bRy6M[W4Okpg{3OxGLG>BRtUm6GK4)HVVDyV8}UXa<^9[YZj6mupj#A7H[#2!E(S5G(&{1n:OgqB:>Nl4VEhA5DE0I?vWRa(!6vy/%)9x84djPG#vPrm8}[.SDIFIxGlV%UJezI3No8sHiIs0^H2B+EJ(Hj&)J?V-YX{:}^?a5YZ%OZ}@jkVyT3O+M9z7#RrtoKfs2=#N}D)qOKIWhS/B7Jo0%xt>H()hn.k0OW=wkiDxM8fj5sHs}WF)kn#ZHcR[qYfRDVcj4lWoa2F.G88L1$$3gqJk0D*Rq?q{2UH-*$2Bdk9]Zr#-=/wj2G.f&wW{y{WvLwk./nnS:dTA8HQzLr[:0m$f$*m?o@jtK>^FbzfH)R*c]4!}e2DWuC/u)RSUDNl]L3r.pnJI/@S!cov0w%))AE@0BXxMsm=e(vVUlBHT-ol&V[0rZxED$2](9R3Wh2ER.jXyTuKcQGpjn7&3w*/c{s0uHPhME[MZXK5el5vI}*09z}bu5&K.p*crP)DLHewoqetMas$5U-6&QSNKlgA&HEJR#9s*+RaZMiwLP%F+y&oHUCWXba$9>HZx&(}@:7>yPfJ$l*>iGCi=KpWMl)If3HVu#1fL0hyCqwP1reZ+b&.ySAb4kXl]{+1yT[*4we%(Kyx2o9<{0NNup:)(Bpa&-?A/Dax/D]qV9e4qYPA4I4OnfL8Wf@+*mgC(pvv#f=FhqV[06-W2d{o1$P51o}OL7OE}rvV7>^8.fChe#%(Ci4H:Z^EEX=-tYX3i@:]=/5Rg.J6#>utd{{p[RoW-UCp2Gs/M6y/(3X0I?#six+.M4d2C>UH{KvMb#&Q(E6Nmjf@7fssqBdlgvX-Az}jX^fgP/Q?)*.n4PUKZpB#9LpE[aG1-m978h9hXxi^(&-@AT>3(&)=F7m{FNO-&dS@C%I=IEUD6}+RN6SPMUv6{+o%srUI+uV>F4c8@l]!J-ZspecSu@=I2puixkm=5P]PcZ1kh-{)q>XvO&]ry(-=!^5RRvy4k1kfBaBIy0L%q=$AOT^93A:=t&5QzXfGxiyYO[/.c7SwlDM?w+DfBK3RANVu1^[]<5WR.TL8!t)aSjP:&DR#=Y^k8J!I}Jon%ONIVu]&@U3(nHtBqUUF(JqEuR7G!OxZP3->l-[>rkjp?q0]i^&Vhl9Y6:5LvO6S}kXOoL8?HVAX:uUdLQ4rMN#kIlf>Ws0]BphX+GD%%q?ROk9s=Gv]jfB=!zzDJPh8Yu^/(-90W]aSE$ZGidJ33tpbs[51R&YM}hV5XBJF6oAY[V$u1-8inJMH.[Ibc$gk*+N6$!JLAc3wQH+o?{hJ-9f2oPJdSL3)i44gELVQb!L(oz/7MJwmCqYpX+Dq?i%$kHHlQu0eU%KRpojLi(-U*&AQ-1D$u.qQ{?ZBr*Mnej^/06alKrp!L:&No^8lSeyZX]qs0u$Bk8!tPkkJZw&*UUDyzx319H90X>T5>9*HSFL3}25Q*W&TLgoF)Th7Crvpc#[u>vH&jR5NAUKwj4kJ>2($9gJ=mF:0>fAKmRLqGkeeY53WDq1y%diC6.L.3cP#Jec#$/n.SUQ+@xdL+bR^V>PoFtMu}xmt3F}ub{gT@7Cx^7IT5.5+::?C1L2mpV7*DbP6h?vrDBno)f-G%NqzQZP4:+LS.5e.LC3Cg(QrLn*4i9*xM(d2@ahYEKs86u4TTGLWC1v^F60xqnEWTxm8Lv}.*C5aG3[>8X2vEimJ(EVsd4}&j#9ib/Xrl33$[7{z9izAgDb8]0dHFz%SFMgx%!*XXiD}K4Z&8)#j[C4FhL..%U{9zcWplCExH<#mZ3DCU&cP{@}TTL%wF?=y+5BW5>E7VUv(3*78?FY+:auPv=+MfPsfy+{=i/Z{v0Vl5*V@KqhR:}{ZB.*RS}Z3)*zEvYXi@/wCYIX@o2b.Z-}>E2oh(UbjsNjaERY#e1T9X.li[H&I6m2{6s!#F%0)XSmcm>.u>aWMa2dqd&Hagp]^YlC^=e/LH6Z1n@hmvi0PmkeJ6k+&gOXvfRLZKC(>Dt}cjSFbh)=brZuniQMdPFb*]ehM0aW(>Q8h-@4>YDfdQ4%A]Km&alZdpvNwKF2X=MX%EJFR#Nrc@[D2ZL0+:c]koEzd$&ZscVQ)F?R&8txU78-}uk3[Z6c/5S(yEOJZ{T>k:QGX*31$2bU?{2js&+4VL-e*!d+-okZc8Hva3b}rgBAqI?L*2ox]@bBrk@:.BWBN/9V]piEozXP8FG8NfKB&T-B-uZ+mbHAs3&i]ygQ{or2@7$B/Z-5x9iv^qw1av[^Swe%3U/Z/psU5tZv%K]U$GlCeP$TAXHP.3z.0G51]B*Jtc%Hcv{$SI[9>cg3QBlx!FZZpb)EV5t7C#h=p[4rhhl9IC?c/LX3>3m^p=t?p5gBUfu^87b}Isd8^9K49EmV?jsfr6CkafGc-O4PJ*H7t$.8@lFY5E&.V!eK-]5=uSU!APT@o^Uq@boC)^#wHR@u*9!BSCMIQTjsGj{0>VUOt5pAwZW39mRbKZ!Hy+n9iWKzrq)hEbEQq}TY[Yn)b&h-QN%uGT/}H8xS<1.!Hhx$5pY?JlO//Lq}3hHptkDfCJOEHAj!Xa)Ct5/4EY<1GKFZsVR2M/^4XH@5s=<$[Kiu%S^.GzP:d2q$90.z:nm:IB[f=my:{4)L*UY7P%:KZ[s!bzt9R{Wxc+KL!ZH[aG=SFX}+e{}m8p?}/qB25>-5NC2B!&^Bs.:[+e>0w<9mCwj)w1@:@/)*Ji@O*lb^M%d56R%8erc0>f>[MH{EgauWw!@:*K72)UWi@PPPO:^132Lg0{Oh.V3&b&b?(crtVQTKmuk5v.#3[kondQ*3csWZ@]U#Ai9tx]EvCYT(IwUAjtgI6HU*o+(AmuL%l1@9KdwV6%J8hwq:++UIXJ(ayaIGR+35hgS<3.(t]26X0Uvs}sr/^Uxpu7I$qoWEOPw]{{tFwdYWjho1Soy0s9U?BC^G2HcT-h=/BiE%z*ffv6cqIxnfr7w(q2U}k-B0ZNnwT4P9H-*Cm(-96LNOI(IIX7@JZp}4L=ErJ7l85>cdP/Rg{5%E}f#S[62zl5&TbBG@0Fv}t$6[uyZr^?lODP%RrGcr+hXDa=AOX2LU#]4?>5Q<94E93dM+cmv=yZGY!z-JspTX-a0dx+Am32Z6PC$rV2meCcJho?.m5GcRIr$6B](HsdrofsM<[c04cAyShE]fkwAWSycZ4g$f&6b{h^!a>#T?zpQ7D6/Bd([Bie}NB7SRO1#S)aHiAbE$C.j3FoV-PKeGD!=v:/0d>gt#ae9DdFt]{IF{nv%=z>10ReCR:.IL#sDZ)y/h#8K.S5UP^KWau}3{2=mcuearKIIksjBH>M>3.YH({gdZ0Kh>m>$m:y1aupShuW)H->co5]^mU6lEreBihOii8k!s/FW5EZV0ta}[5C?)G+<&5X6J8A!VIbaUnq)T/lY[&/#E/Xg9FX&?ikBhgw*>ql>yQ3fQy6ztWx2cZiUJ7K!jGdipP4j5>-LdW32}.K7E%VlRF]4GC9c[Cav[1*0j+#aZ$G*AoT!^[g!U=!>wIPOeu*Ie{DN{E@D)HmBy]^Y-G#+xZAbBQf--y/g7Z14&IQ=IkMK&4io7yQ)JqhL9H&rTt-ZyRf$b]uL!>y97}zWv)g{@enqh?cB6IHhDDMUfbq=$Aqt3l4iR&Hw*!/j.+4gkB%[xFQZXnWC2Wf3Jlu?E^HgRjkX/GoF0pjRGvRUD5d!LAs!F[y:Oj24/cKU<02-za4$LFK]/BZH6JG*3)jL.iArcMzaP:Y?p(dLHEColuPvvWo8GoqmJ@-hoU%K^3yJ}jl@PURo-)ZuJ/@wmvYWS&ruek1jQnhEmtpw=]C6R$UO!.c&0WnJnE1esaKQ)2F+Q>2-mpqB2}.JxqZhQ(!C%x2yww6q2cDenATW/R^fIMS}v@wR!+eyg4b.ik)}73KNjF/2Yhi>JwzX^^:/JhQBenEOou!H6%{>vgc]wep}rzN9<9nA-?a]nXxMv?R4mNr>30@@Pz2b]MyH]7Xd!GId.2*8YZ{dlPDaqzIQhs:XJ=pL&O+ubmz^0oI440+IrfZAY{8qn6wSKn9QLR!y5V0!WHr^C.$Zru:}+x}2lo-hodY>}rZ+PkU=Cf3nh:K98L+-y-JtCd}!cRic}1B?Ic@JZd?(]]7P%ax+Ud][]mBo&-n%]nLo1Pqkvr7n6vc:}l9v(r>^Wg%h>9BC^$SoTDD-!h@8@n)WXMXu]Y?yMpH%H4Ozs:yw)vIV(-8Dr>w+*3OK3pSTzvczeaO1n}QRUI)Yaqe^47A>m87KLl6$xt%1?b*)KUw!8>E>E>Hil{?U@oDz1lSGbH[NrbHz.>nzd{vs/Va.AT#O[z/NF]:WHV$7>n[#IMiYJ6-Q@zOE/vg(MpnWLn-dTT3bnaq)pq#7HuGhKo!Sf/MdTuc&NzeBU[O#ebmI09:p.sxTvt9)RQTMlL.{zSzZRwRu2MGAsV2TWOjAj=O.x3C+WUn?2h)[ybMUpWOYg}XOz{IoXx!WAg1C8qi$}F:Ziifr&O4>dkOWaRKu$3/C2Wmua0cZc2J[Dut!jQzbsk@^a*S6SZDy&GM*40r]L^BCyh.oXj^69M5Az2q]KAn%[v*z@Vm*OmR1cC*B?r6MASbP@@2Lq(^oA+RCTM%hWsXuLEw5sv8wPXC*HD=+dT1t?&Ski5>1nf[>.=.a)x@20JzB%&W.g-RI/ZjD2W^KkXTvN.aN&RBnU{#SZ%}%K6{0Ud5cHCa)pjg->7-*{o#Wvs>9}QW(H8&}o%XkSvTI1>d9tA#y5UOLgb9#Z(E5L8kx?09c$OQuM3DR}J}w6o(-I#2joXqVH>W+:qjD4SYgvlJl%UyZQi-Nj!(e7!Ag$@oKJzqQ}jHW5@1HkN/HTqXL^(>!HJJ:{Q8-jWOhF!r=IBc=F=5FwjuiR?6axu:k^?PgtF!@gzssR]aoLLz2wQi7l^D=sLGeeOeEwnuS{VB0%u2+(C=ps}a-JJDe#+()3sH[^JS)?T5KEW}P]*w1eR&ZUV7pUkt)6YStI^EV0>)V^L6U:Srp9i!/{6%nMmx{4r@QJaXR9rN?YTE(mfBYHA0Pv^x-x}Xbgl)AL1Ea9Zs]cOz8^r9p(8vCfZqyLI=lUwRF1*KG:Q8RtiUx!g#wl5^H#0]xE$*z{<:TG@KrYMtHfiRqv$e!:s{UPtzNQKauF(17d6fice5%=CPm7)l/af*get=e(lxkCkv5>i3}%[X@rbJ5Ne>ZwiR:)UC8}41!8*nlZr+:*tDdsLg9^-SX4]{X5+kKL-OxN4UBB&oxl}cJmfqVqeI78QszS&BoUt[C*Z*-E7o)&cb[zwm.90[BkI8BB26I5Q]5+hc@Mp1oI#ZF]QFUINa+6^X(h:Y@C}T2uTrS@TlS3mE[q-Lu/(ml+Ga5-4CIv0*H*bJMFx7:cZRG*r{qQkP5feAN(lw4NVP5&Qen-:>?rHRUy&F:==%FiSxe0Poz&h/9%s8RvfYpjiEyiF[?hFe1w@%O:SO5d[{?tSFQ=ip@HwT^iP)7{r6OfY^w(nR!T{HAWwys$-(r@wsS3$0pf$.N:oFfHEYt[F:1:zV:!}P]ROmhPafwOzUV!efvu4Xio$53H!@w6*NJRwUkY3:O48eJqcFc]9zC>)p*PsE$SYGx*f5z@)-3lg*>qe#t?DR%Y>=[sVqasdo3B/e=yew4EU^YEw*d(jag{O7fKnjp#XNR2^tB]+@4guLT.f@w9)-*ullnD8bv{BzdIYywwGAUw{=V7.%/w.T2>^a[4jkV0Rwt2*ebq#oOye7v&jM-!SW9P.Fp5#M]aV%HGb#deSH/:])8PiepbUE$S:?*Nc5q3Me8csXjWcnCJih+p:O?[r!Qaw!c!&N7}Ex.9*{pgHYO-e]g!iN!{02EI0&9ct>+>kDt$MZJ(S]Wk6*H^>j{ChOpb^.8ltb$&1+v=1&0cN-&MA9fbUK07o4&SAiPQ0k6?-G:^8q%yiZc&eX=9GI@8!Jf8mS.R:(wtdo#!/qnsQw<6I2*PM0:xIza42fywVx@+NC)/YPh0t>*T7$)8@RciIYtX@eNMpgO3yg+uZh)7j9G%[Uz{}e4gP2)7#wuRh?Sy.zctt]hU4dP]=Cn67eP-^v*)I]8)bGYtg](%^}c&:qUa)jY0?[r7[qd6o)[EC(G-({v%!0eIZBTojUqNcf:jITC$ZDkEr8q#Mv<5Q7!B1^[0a*0W06]bwWtp@15IICPBT5i0HghqvId@A(4K5=UaX*6-Q3)tKGR49NghR3Vi5Sw-ygv4Cm!lsCQ/ji+!wkhSHNd*VU:b7eq^r(L&TOfQwAy.x17jO!{[D.)lrxc(G{V4CNU)qzKYKZ31AVPCQ#V%tWDVzQ.:ok$hK{mhz@$6Dg!d[P(Om^JeaoI&NHUZ9[B2=T!^4XB#tnthPDAy]3vppV@38:YF=$hZjqVu:NlgoQ720g?n9^[=lj6Jkpr+gbd4&sD(sQdG@E@88Eh)}W5d%?=x>>rf]Sz1+tN+gc/LW}crd:s-5^YgS/cPik[okSg:88t/U:66QlgH*Wwjokj@x:ikATNK&dUuASpnWYG#f{SC}[-qf@X!JdB-YW3NH%8Kr=gR6A%CO&(d9&iUS7*?.@zCyfgnkgol>e4CsOP)*iAm&q*WR^2-%}m}P.5*siD)!ICXaq%Qq4+XXxT=*4^hBUx2>oryyDD0I!%T3?jRR9mt!Rw8rA+JWA!Y3-s#:b?sZA{4p0-wQM{s{OHogu(oCzVDnWpIZC%Jy2xxpbSgxedS&PaLZ9fF^]I6sY?:E+M$.EmR{DThHh^FM8M/$78MI?nb5yrcq}kz6k3ckfl:NBVB[^=ujkX?J%Gn+2urigEzPc9qugiSU#5GcR-2q0P6ds-l>(fk4e1oS6<<.OVdzS2d-^)zc@#c^flhTb]bB5[F>HV!CryHt52ZpuYK>D1Rx/k8C}=mQ/3Y>:A#2<3(P(-U^Y}VgTC*]A{?U^x2D.#kYhmGI]M{lKDn94qrOT!JgqJO=PH6lb&qb9W!zC]Vf&iyHeAGExBJ]IaSA-Q#M7@noMFqE}bg}p9{T(L.4X3{a=(Yq>a]fXH+^P7-]7uEA?D}7j*1-c(5T1oj4V67J!06cFM0x}gY!fC>pNL-tjt*ruM!1h%E-&BR*7<#CRufOEGtOR&wI!+mlyb6*irhedAY^r(&-*bj3oo2wL2A&rz:RO:*AYTWK=3+cu{hi:pvEWe8.f:$R#hcK/d)Qlq.(wR{2:CTX7lEXtSFScX!Ggr&ed[+*>y1H)!rFbPhVy[ZF@ZZl$uXg6Zk}#g*o=pxypT:Z:t=P4SZ=?bQLfMS[MNBYXp<{U0[HcAVnwilF4yNyKQgW-]xcH+1tc!vOrD>!PN--7]hMGy$Xuh!Y@NF2E&S7^=y#EiDiiXTSJ7e5aP@1v+*mKnmZKwBB:)FB4bAjV:z4+SNf$:E@5r].6h9Ty3CRI*v8X++&a.>MdF$wy.MKA}GFPNOgjRk/+yrZtn}[qlw.9{Ta:(e@.VHrj{MZ]Y/-2y4v)(-nvIjOCbySIKckJ#61ef2DU>Wf)FM:qqH:0.Q@+2mc++bKk3ktj*&0/AcW8VCSpJeD)h8#mzMbYip%njSrWRxxKDS[uJ{*)LK3h5Id+S+3%[2-(pOKa.(kEbVty@(kwArA3f0o2xomw<7ia^T3J9=Fbh%ic(uvbjyIi>}Ei]+@8oA$${:3A3{}(-0U8LBDH1R@p!^aXA].fMd!Zd%s9[ii{lmv2#0U<1ofJ98E.PM5nq>K8Bk}{gwQDB[DFkvvSmGZT-d{jWUqYG9At{[RfXYFS?=7L@*.G#VZDr5jG4vgyyyism<3LqnLl{NhCo(O!E7IDN{e&Rct4iQ+kw<>Lk5d5nfg?s^[bBXepR%BtJTvpE48ahBGhiFd5yby4O*X6A(FSR3[QRHSCFWWHuymmS)-f(Z5+!%V-AMxuB8s)PmRPKB(:C6TeLS0TVD:sQ3fHsnjvO<::@l!S:+Ct7@c9u-fbZ[J?Z^wn?55Mr0iSJ&bQ&YpbQ.cg0:I@=8diX2L1Iwv5]1aNw^*SfQMS1l=#.1BD5mOCI3M<1k^<.vfjIduz+=ddjL!MQjhwu<#UmjOQjXf!>zs=7MW>h(uwOUT)Sg1v&(JF.A(y*#8u2tkN>H^9x6pXYc*BqGnVX/8gIk:WEjH4/eOhH$:7J/hhpR4Z4@F/)gz]gaYI$M@!n]=NXs$vtn?^n5jhlf0@5lEwstMCTMvX1Gdy(BQKzc>&A^ODXm)!t-UoFUnv[.HXpY2vFjEk=naUef(W57J]h3@(arr^a%IO)*{(?wQYy9LzZ5qFfO.dcuUG6{93lxSFL=44V6)>KPa}J^a+zv7t!BkT]=(ZRM+-T{Q}iRPRHP>G/Q7?&9U)0NXy{KjrDS7H^tNEzsP?P$Wf%llbrBig>KpFsK5}a/KwZm6/3fl&:pSDHenFx0&OBjzUkdi)/H:]75Br/QiBEFY2+r-:+E&R[BY{A3I/e^p-n0YYVay54-BXCqh>]2zpu?G!.Zc^eZieyBnj>rf]l1<}*OdGs}eQw5Mt>Zw?P>%LE4CX/.=c^oHzn]PhU^+^#4MuUpqEdYFqjd74bq[O>4er?vnwy]wrR5r(aL?PvZXY>p]/.sqq5dTbv-U@2O^YLJrcrkCn[WiHiQdA6$)o0b}U6NH9B:xHEHGIy}F>B1%pfM4LS[$T8w93vNdOUW^*a!pg27%U9TIh+!p#R}T{RX{mY![kr<>$E^u}%eg*-a-TFGY40XrP1A:=l?L8/*fM/ChNi)li>sVqix#CgyRc77-@R-].vqqyxJ3W]*PdJV-TV!+]WodeWX/%GjgX7mNycqHMhAm-[77B?$B/8o*$G)^#NDy}2&eF2KAMcZAJIer#s[G0TTZIB?tPHGJ*#Mq9f#*z0[O([Rve:<0yMA*)wJ]dwweOG1p4JjKu@e7T]4W/3WdtI6j?C<2nTAUc*3[SXT$!oq}Y9wH3!Cd7]cQ&(c$^b$H7ewMSRQ}%vY#ages2b[$6]zAIK*)W)=YXk98X-./^xX9tY}3GQmL6Lu1:/8k%+}TF:H)Qs0NG2+XoTr7HW3575}Ms0U*zX5Yx)TxwfmQsACsBids]jGjPbM)O1qut@)ttx(V@%wUw*=gz5HnKGxDm8=Lw!POvD8=T+JCgSTXdmFoE6Pj7t$AVWSRu#=rcs^Qd1hKZos.kFsPqQTt/omWC%a%Ao=^^TEp!2E?mCoX@1nfhTR)&nCUv>Jue7)Ej5^xc%CW.x*S)E-k*Iq/g4vfdvzz&f2/MBsZ}!pRXn[jAb>jcv/*M0eV#koVAA.cCD4oRJB<$ZyLP4{lm.[MLLdy.0h{7=5jLByoPg/N.2zRTvsszFt!cH/jzOyO/eS(95&GekLGH]whF:cPDkXjcRYR7<2VJsR!p@OjwJ1Gr@WasH{hKlX75#Nn.EjGT*A-3iGF)P8-JAcKx[U?&Ito3HeR-JE.u?(MyxyOHSlWZX==&-.YBxo2(&Ad&%0sR5+Q8llW&K%N-K9wlDJo0nbGvM%/0taWtgU]?/v1gJCtkYYI&02Q0(>6bK6Cm*]Ns0X2b$a#(Rm!cxNCp9KCV!kfA9akLc9a&XK2e]zJjJGr!d+8Jp?YuV@.R-J?r3$tma$7[j{&vAX{6wlvZI!?-jG&5M<7muIat797^?u6aApkzF)r5GwlBz5@WYl.mE8#-uRiVZZbUS2A/7=c%ES0Q0:xPagsCfAiZ:>LcMzC(v?8bHQMzNFGTW@+oV2]@4YWk/z^L^i0:jtn>N>S4((5hvJw!kHs>ohU[(+FJ64PyxiOM3*mm/dnBNt^=V$K&9A>lnlj8oSz*hRez0s9J.G6}0fV0@XT<)&FZmJQxMHIWofH>F/.!3pI6yNR1fdeMMs7SEQ(AMsTI9Ufwiq#FgR5>H:/x$my4Q(s/1uzV7y%D4A{OUX$&>w-Yg@AS#z5akMd2u>u9ab.1b-nr0KqSwLs$T[6RKHoPL)by%uBEr*V7K03zsE5)Zjcic-(!qzp.oc*RUu-1>4X:tl[5JNKxizF.OW6)p{o]Im]reD-]dC:NMDqG##]vOQkXTlU8v.BhB4NS>e40)8fygCx&Kk*xA.5h/VaKfMrowVBo!MP*-cd)y.LAww#&gloU@#/fegX2u>koa2O.uZHZHllzArh^Lsx4&L5U1l/T*d0TFr(0&1r:]hGR%KznTY6=lT!{Zz*BQ8Z@$?})EoL*ilGgdp&.+?4.h9O(8ZoDD/nJ:UuIt@Q/][^q*mH{4vO(![4vu/pk/Dv8tZy9yUU54H0u-m!WC5dqo8[#r==^{dQ(6=0ZG{E]CmWJX-L)0}PPsj4udU0v5Y^g[LHmWgq13RY?4>d[w%gQWDado.J(PYTRh41Mt0ONTf3xE3B$Y33ate?zV>T^dZ?iAvhXX}UJBOSx>)1=zAl6lNRzN/#@38!hFMd4}r+TkBBBPd8S^VQr>MiSE7MJILRD^ELrhC=r69s+y&1DdvIr=AZ5(EmVPo05em&a?C^{KMp3b>)L-W*cdTI}7%s+iP[W^D{&[wqE-7PkK2F*NPuzn3n:Shs$:vXR9O<8VktT7fhRI=xEgFBQy-qe)%663Vyy:9v?(-oFgD]3Aivsh+uj)D$=s[GQ$4pRX/(y5AJG0v.t[DlyP3wUf3UR2eNPI1*-nf{m$RNfG5J1Du*j5KCmmxnMvEVXBUdEN:S.0e8X%tY=D@&CTPX1x*d7.:4I7A4Y)LQF(.PJF<2{b[{x=J[Ql@tCZ@TY*6:WGcv*GP[%KCA(IZZwuo%=TrNlIuru!vDtb&2:zM]Dc7$YU&YiIj5tAQreyVe:gge#3&mBbXcvfcN)MS*s>dt?A)oDOH?JRqa)eQ7lIdzlvOW04uIR)7?sM=xe*dA998VebImW](!E6B{&/RS#r@%TARjPkJjpZtXOXl4-BB1v$#pnL:ub1Ut?AuN]^V%}7lt?[xYw1ud!q1b:?nY6-vh!C(BcQHPg+(%!)8#G-Sr8QP8Bi#TmT1l-9N3Pd*5BKZ%>@qOm{1:9jcf^$1Y@6n@5bk=za+pFF*b?Ko7br!Y>7hm:kG[#E@!G3&iZAB^%d{93@uGS/@BLlo?8l$]n5f7xw(&PR5{KRCe8?sF??T#/SqE1Gz3-L}JqvWN=zX7bh%F7Y.AuM#8OO.oiXLMi3]XQR=z-?>b/&OLXx7*ln}6O6q(@b[^+iB{XW%b?f{VJc?9)R#Y-MR9khPDPm)yNQ=Is:/X}@PsNzR^/9*4Lp[y0?1k%Q/PL>BVVhNyI%R1)3EYE^X?D:e[gw[#?KoS^g>!)bzY4r$[)]AK0g/z1dS<08V!-V508%6n%.h0/uU8l-5)*r)ehsZ*JvRaEun[P=>iIHR8Jungi%DddPjoR$MGpI.(^x!5JToOLoXYcN?SA4K}mG:1xxo.7{/XQDJzW+T63zp.2aM!2C{{bP6pSK!b)/lW+FevS6qMb&W$K@4y3(^Rp)du#jc:1Gl0v#mV.CBBaX[}twF&dW[GmR=[{NB#OP7U>h1fzKTy}k^>>a)k1V/{r6v1LXVYEDGxM1.OCXQ5:Rsj0?dgQ#P$B.TOU(AqQaDfUVq{pu[kXi%1I:./Z)NDLP-kj+KD}l]Y1:Zf3D{AZh8Bq)D)7Ne+6c^E7qw?fxD1H[$S>>+5kIR06!z[tVW4oL&ift=!Y{8Pl)lv<9Bd?0xb?:lRgy66r^3VA}:uu{LmzYSfnRPQyG6^dqD[Q}Ic0Cn:+D*v[&B5kj+QlI3oR%q8m}O2=O5AT?O@j=o8[FyE)x08+T1rwYwNYb=$S3@.F6.D>/O@:T8OsTKec%TBIs8t&wvb%v$/h]q^2Fjn!V[}jF@$p.L#5Z/KBcuZEFM%J@aOESqq.XO9B$a&onpsDoU{4RcS>oyer#PNByI&/F9^#(.M4ZCl&y!30npGN!*mUt3RM[0K)=@hx4]J{x/2Vxow3E%V9G*0MnETDJn736Cb8k&X**lMKl]RBW<#]<]&YtIr+pI+mBJsX8]!)EDfk16Y:A&@x@U@7zu2FvBh.gZXv)o@U8Vc8--NnG[#ZL7{ra8MB5P[MS0A:25#+!-N8xC>fN=f9VJjVXwbo6Dz4}Y-:J**MO0HW?sGyQ&%.eGa)EnGk}m8@a*KalhZ/S1K&HvS6pY>z:j!tYD-0CueMJ]W&(8>vs3RJ+-1M?BtX->W^qTndJXY)c$(TXwsauV!}TviFi*D9]yk^jU=[P!D+J09Pp?9:JjXN)j/r+KwIqphl}U&!Y!:(wm[NnB%YHeCH/z7l9WD+jqJA:b5i>ekNSVp+HjTnM&7mPn0Vuvbpr%Hp!QVC1$S8MoloAuT{qY-gOjC*${Uoy(4f$nsCqKM1hSTCRBk=>y7fpYMGY@N(!{RP^?[OF+g>xZk=&$j9fO=))]9gdRwutIk./2AI@k7a42=jrN=f7?bt2(itM[A6aU6C[uPIavO[P/yGOIOY66HAxSwD6QZqJIG4r<}{(g7?5ZK+wH4?{QZmODH.E[^.=YJK1}+l@59Z(8h!Lz95FKC)!/>qNYN#8y:+{SAd$sp7UG=!r:P^er<]D{4/%{GlCRPE^GD{%o&)-yRRD1f[{xQG6A}A}/LIhzVb5b=^3%r/*z6:vJ98]GLr0^ujphZky[p6ZQ[kV/Z%M!TM/LwwBDx)V2x(9DW{(C$$=.X=3+/2A2mYpYD3JGGBPOeH*Pw/myui6Qw>aDwX3mh=w)<..WG2fioTUNy8oyv:2UcuC$*N>)IIy?1LIMYw7z101<@JR1pVV)K%z./!P]kItsKrHa!4MdKsald!7v.!pnR%^a}l^LjL(2H4-+xpxw6E=v:RTcqjp(w$:ktY)O&rON8T0Sdx:!M2N[0SL4UB8/Jv5>]tlGxsY/O$N}PPi8rF4C].f#Om0dFx?nABCA!*4rf[ohHzTwy>VEQu789>V?8htXl:NkGZ0Ve6l%nMBJHz6OQ4V[$yO>oH0]!cN&.#npV2RG4m2jSK?NGF4R4GY*/c#U}DsmA0O(Sd&9u&lp.pg!&rX5HND1/4np1Z%%pPlXoRvI1>fhrD9:WIvY7Z!//Id*+(MF=D}phOy%h&v=8zf?:td8XnA@-TCguQk8?SABSWh5K03?QCB!/&4/GZU>EzeX)}?$U}J8q<2MNMY*JR#0MWmv#dcPn/N-jijNl]ng-dhjrOmqg*/9z@Z%n5@Xl}*novx@(kJmnjOE#FW]0:6djSpa}R1.]8ky3vKFp/YsiBo(g{=YQWJZv+X+JPC&6FK2d>^Re9whKx=sNU9$ELDDwe5J5y?GW!KcPR%o({?9fX-AO&S3MS(FC5soY%nEe#tt3^+6cKFrSYAcLYdo^@vWD8#2W@<5.m/KbG&?)M{H]C[-y$9c=Q0u1e@?CkZAxpE1EaSpcu9Rbl-vQvTjMgpNMUWUVLASc-*)t%xO*HJ5I^@/6GTWGPPCna[-(x=r)gSY+TemHBl-z%jc+o.@h-B6S<::1Rf0%xCCH9IK^DX)X!Y:1Gch3sXM5>puY^MwK)HX37m/o}>QR=a3#?E5Kw8HB((s35DKs2tJ4{H]#dwS>ZLX/UbIf#Bv}7bo3LJHEfsjV%ueCf.*aFtAMY!vsgP86u)(:o.)3rr.6^VOvxYmnp9orLk+ak9QGgKm<%=n9H%}WsGF/QdA+7bWkv84n4mbZSb$sH}9(MD#6yW7VFZ!qSg[h69&q703c>*Frt{GF>4Po^>44rv{b794sEt9P:jx[YXZ:FZ0^{:lc>IJa5Ukd1RofbNZY!&^yJ#cbL[rlC%O*E+(0^IeM@hjB8hl7TSeBnO+HWfgrNFyTEQ1W!j3W}eM#24RUUT9nVj42w=v-Srd7$Ni4/pU(0g+luc0{og$l^E7^aK:h#1z>yt]nw:icq]vPoThbBp^fM[ZXXId#Fn+MAy93}Se:dhD6j+jq7C:U/F/68ioPv^az)Bkx@^HOu1WveYG^MIpFq6Hc=3uW<4VEejJi*CiIfyE*A:kEX>NNvQdt{4g0p4*qz:dd9.H/Pg%a:7g9=Cu/>B-AcV-Uv6cF(5.W]j(.H0>d%E&v@0iJ3hvn@l13:Fpz8p$cewv)Q*m=Ez5x}x^/9Iu2^4KN)UvrqD!+9A@d@d4v5:jdb12sp.mGG5ARwrz/UU/ox3z>{ewF6C6S43?3U^hHg+xkiAT7T/i0LmPv]j#RXF6sIxL846j^B>TMRHn-p3iwXS?Nz^gl8D3ZjW>]d!W39pf-ytdUYg${5&r&)OXEw4LR)bQ9a{H6wJLRN}P*g@YjQT2Q[d<6$yuZ$j@L+SYffbGqNvIlWZ5[fHC<)GPH5oRr1fYrTM{ZoS6^.jQa<[87k#6tYmwu6LX[5xSXDX8gf(!bFfMn14GB2HDRGwU1gOEH8QS)7Q!sKJT(3m$H![bSK4nD9nR]B98{+!U!Bk>)<+ruVZm(C:f.>{i)==66B-zuxew^!r{u(%XW9*Z^@lHy5Q59@Xtx?t9iot=feZyh*N+&Wc*!.hHJS?j4![mus64pEVwZizV1aj5]IA{9$:}j@5{%3UBySwERW(4Z:bfQBTuXgLRheFF.YVguUv8((dM%<5Ngp{!caMkX1TlN+FVzBhR7m+F-JVQUQ[4?rkwWtZ1vlv7T7r5T7od[Ail5Vje^]k^CY+w6at@C^i:a-@tbXjI??i]2XBD[&FK!sCh@Ad:Q=-CWs<9!Q=(u&b@7Y>=LRd{(?G5pgu/1*t(R*a=BnWUyX5C/YYW/fKX6Q{tsQS4afK](t$00w1$2HO)*j:5yf-h^{WU*d+ert02Jn@x/#I4?2{EpF8XKzifFX>a1c8eggskJl]7w&#E4IpvYn2^[C4binM^Mv6(:bNl?H1K4mCybc/*L)=ke+!BcfWgHc]55{P!$A-Lgj7(51i4YcFN}VVootEyNl]3U5./rR*N.?RM=76>:KEg@F8aZUURX^GaoUYH>^ta%tMZKaV?n@1?V5A%qe1-Q}92G:va#w^=366T77{THXSpQ[F(S04ovgZAyg!^CO/SkO)(zt%67C!pTSdTTc1.cel^Jk[8HBjFnJ^Si5jdFz&n$&#jS^g&Q2anv}ftrB}E8^Lt}@-{5XF{nSRlTw)!h7%9tWZgEZkkr&MKVNHQAr7g:&%u^%l9AcV&rMZ=I^0BSss]rWS-CpkW}>xGn]y^[PZT7j@yFLJ[RJ-q!Z-%YK$B)O>^DXu180]WmI{iC-s1WHcY/4}?z$Xl[-0#?S}?Mf4jLrlg>:r4sLFsYrZoI1iqn*1*@eTF[v3PUc<9PIeKrAhCE#PGBC]Jvl#{bH])2zR!3*3m/1xh2bR[]Tnqx!Oc%d#1a<{w?)7X&{7+6gj]08*[X3BXc:RI*n/Kd-W{!k3R<-X]T=gdF>pAzg(agFke]4fJJ(FlPQ.4uT/O<>7HVW2bM>3PCq>.&b?=k+Q3zzz:kf92]VLIJbY@/}?b]]!ZAiisJ.3gS7N)!r.FV*XRr.6)h>>JRf7[m?kC*aOZ{%chF5j]B-6I!h<>O{z^uh>1@=0r{U[WP2^H&F&EvBuRH74Ower+2IhO/TV7BB24k){UztUXvn)6yypWt{pr@fBhf}9vo4^boI)@8Y!8ruOXU$bn:}NMgMWX)4p+X)nFJvl.9>o6BX((WGu6b!6f!bTXZO0m#+RJs9i#COTDKj7E[M1nVI2t=lMe$S%X@blbVfFd9ovL7c^A{$.dVz/#eNDmsGrx4g6nsJ<}Jn.VM[msdgy/2H9/#gV^z)puOwV<-OpaOW4$gXScKB*#&}u?&>ek>A6?^i7IkV^P1tUI>y?+sPGX[LCJIj}5oFb^(ST]i+E!w3o/.8snGA}%n*ahj@{>rU4v)d]O^z(v1Po[e*>?XOr0*]7t>?E$s>8(+ct7E4E0@6Sl*jJPVAJn#wuE0JI4O=4/b(2g1nf^0[5$I7%X!x24!2HtEl[1i5NJ2OUbmcLwJ]!dbYyO!*9u:-Uodld%c>z]][bYN4z%C.(Uz&ru8dMhRpHAI=:t[k:X/+H8ZJ3}(I@Xl.t5vdwl2WeWaJJ01/{Kb*wt>-EtDe:C}X^8ZP7dE^4X5C>.#dp}fmdEc@/1tLi]x?rVCeLH?H.LtgsbIhw!IH^(@]ebyvOrezr$@(!%!C^^0VQj49A{9J-*OG4o#iz+iP418DlA&iAvSK08*Kam4.}L4xM3vo3u^X9?NdVm!{O[(*3)x=U[=x7JC{-+?o94@wLK/dRl3OhH]UISA>)Z<[<*d^K?c3I.$aK}F(C[0O6WE3GC8Igbh#?m[+LzDN@EXIMj4*SaI1jQ::VdzC&hbi>{SqeL&w0@&33%l.&$V/k]h!y=3qxH0U?]&@7iK6{EZFoa1IF=1:^z1>O]ewL.XjeXRO(k0a/k>Z1v:!*18za3xiwk(8t#ApV6K&1VXydDA9[.Y0Odtc.qVw8No[=h&JfOe6R2}rC2bd4v2)BL!G0EamDbANW^)XZ{30=Wx^D1(B{W&jNtg/*8nG5Mtw81P2{fKI3OP7!gKYRQ(RzIzH4(pILxPeqa^yly>r&+B>agj3?@aT.cKxZjUVln?b>jb!hhyMfYVdQnJUnx{hd*zUiW5>rW@-)UpEuGT*HaZjVLJcrqut+&]]}Se%E>jJaZ^(Uojp6^#6+:aD.Unou3g#PHf7mcplZ3G%AxFquIE:[(c[]P)=X24%GO>4]@=yORWV/oUauWKc/sK@V9a6/4O+[pWWI8Fs:g&uot:@m&QopXM!m5MR(h+rY}025WO$oT:30v4{h%xNVlFv&$i@V5&:EUtUm(0Ib)YvpUiI5{g[8Sejn4@%G>eZYZEtcX60056MdQ:cyC4!7F9{l-eQ05p]L#Q^h}0>4mF&Y3h2nx)[<+jRR#X3y?}=icw=V}OV1dn-1F4qp8FI>4?S0A$UzqMF^k{2D&4v0jo/rT/h%R8>^=+qFc?Sm5ieZ)Fy@VEP?OX]7KrBmP:(!w$q7kJfg[A^Xq>dw!&IY1^qu!0R$#Oo](#O98l7osns$R]DPZ4?hd7uzZQ^tXhL)x9gQswvl3/Hruq2fzcN4y*J7e}XiqWAx8itnzCL*}nW9OUSwe4N:yt?TbB0ulz]1Q-.ab8DhDoJVSn[R!e@S&du2!=}/UUIb-E>ZzE9Z?hKU>>at3q=RQZe^[wyz6hy$)<5/aV*w0f=ZWHpq/qQKhDkl)Ep8hJ0uQgiA^WtEMQz*>H68$F.e4oM?umuYLZ:IwwY9]3joPyOwDXA[<1yGCtGgZyte7+Jfg?*dYmWpTe2VDIi$B^G!6w[PW=F#FHls&vAGw*Ozww=4M.A=vpDFG{Ej$tlwKM6w@io!jY9!tm0JT[mRWV[xMIw>Z@-TSQ1}kn(hZa>dlu).seWy=yR%kts.ZqU6t[Y@6TP6#o7wW2On3*ILx(LTxVvKq45Nry^:^0y+C:]v@dfMFH?$wb+8:K{/&We*z&?-9t!fJ2if8=x^SE2-VZA)=zzom+/z{.Sbh[%^B7ak)68n7-Y]3zM({X8zw-0EUZwNLZPpsa90[C@xq1Q=o4e=Kt:PXfD@9L{yLG}XAU+q0.7Qq@SX5Q?YeODe7%7]r$[GA1dT+!vjzSwH(NYxbP.idD&=2CRa1l$myZifXxC?3H-7fJgE1*J5JRIR)P+s43YjY!g2bg4)fJ8Z4({EbulS5Oot[!HOu?jTh^eMm]ey)ol:QxxJhRW14IOfF-}pd17Ge=jaglQa>}CBI9vxH3SC=:JsAzo%!oqymGH*JQA1JJR[C1Ve<8LTRl]*M9:5Txv/3-[+R:4TdJ./r]&t()=oum876lHVCM7h&O$kvDnzE:f]janTHZ0n:-Gs4tT3rh}ZH}SCHTSaEmL%LM@h?C(aM[f-mkYfz$/3to1DlV.IT{{FtqjnO4dwm4SI+.YEmIfQ*2RO*!(?r^tf{0qhi?cq7*LU7z2nJ(J$!#Tsb[v4p5K?A*?MIC]PVaGL@#=W}-Z+VrGl=T{RJF#p=DP3>zN4[J&&e(61xaMNdwHZx/%+HRu)hh!8QxA+/5?e&l%F=S?Hhxr>2Cf8s2yWIw.2*qRCvSSYR-=*&F!O8=L^g6bB>57{#&HRIDGPRS$)sS3o.w4g4VQc^BP.k7F3qJB0JoFq?j7[aPPWY/Q/Fb0Oi<=tEqdFjb!Ye>eKv71qNPVa!l>lQn:O]PtfcX=2mXH%8ju}?Ly#y{Eo/QEri[0P:shPUfx^x[1YP>]p]*=CPp9fUohsqHk27IB>MkKO:e/B(GV:/*y2L.fGpON->cByykQ8tWKNp4hYr+[H}SPwK@=^3grVz(Gpg&.iolU#<+?&S#Oi!eW441kVjT*7T+0A&Z@H>?kLt)q5yZ>yCDJ2nv}Ax00Hn/S^Tx5<88lNe&1E&Ma(A*$EN/pl95dFWF(QLu)%%}t+r/FqxJZl3hSUmWl8snkpp-U2(BEPg5j&MiAA/pZqVn@wt3z[FQ*<:kP(ggOC7bN7hrMc>^oTYJ.jR(xIogD3+sw0Sv8t?yapK:4u?oVpAi[puw3HU8=cDm8FSB!5HoFLRnIB5426^Kb{03mgBkw&nCur!f=}Cb6bBZ-H{h25m-l*p?:pT$[4}5({OxcZu@oC>k{Cm/omOdBg?CF+YB-j$5K7Jhic2*.ANO-hTY9Co%NYUqAu!dX8?4yxR0^oFK8q5GQUVNkK](MKIyu7)#CTqwwq+HI6aF^K=Is29nLCVCd{qq{JaY}KuSwPa?:Z:>7a7v/HH/.p*2nWq@O}MWV]ntNu3U{WgufuooZ{}&pk?J5{Jb[*Y}k/l#@diI:)=bUE)SQhQ*n-k+WF2>#aKV.MQ:<#ZMc4zVVL8o%dY:rN)t[@q.2vAxnJH6kf9/]4kV<=)o+BhY#>oVa2(8gW}Fu[DP}tt(jb^wHyLoieQ6)sQvGj(+Lg^BUlSv+VA4JFAqQtf*tB9$A>Q{Pn1wrrw6ZS$g[4IHS45Q)FCmVQn8qZ/e.33SmVL$TL22-slN8SUoKDgW+mC5G4BYR=qA:]=BWdPz]5H=0}Pn)hZ%NX?ce5+5>n4(xPp+{QVxN&2GTVxWn-A$TqRuoPO><$6eQgXB[1P#Q1azDnw>Kl%h+VT]2>t+67C:tYTa]VUS0&!dqOu&=n&{Y3E(>LKuQA+F3^HXeALQY(7):-Fu8N+g?H6oJ6k+$P6*iANsl>=5%NT%L-g3[9f9n+%-b+MGrOuU}b5t8kPvj6HG]3luI#fnwz!BAL3GnL4hE)}-G0lwCo27XLp]8O*At2EhiT^trt&eGWo4z3NIgQUk-bS=MlRLy7{UogHga]W9=t6++uyeEo5^y{VTUGIJmXSN2{aswSc/qswWd](%%J8Do)k5h!GGz6xZtfGJj@G-QNlG*PiMKL=evHQ{AB+VXabn1LFCUA.qPo]w77uIK&Exy[=!17T2CyDR)fpK.r#yZtr7cjOT?(Uxf[N1c-)iO>SylMO:sd9+:El4gR?P4C)[S-F={@7#9@@kYwwcCZkbAB^6Dlx.nQ{)]K^(<4D*p8zG/}3Tg{B*Rv?+h/VBDo$}kMrSs-mn(h/HzQcEf{ExSWSI0No4q=L7Bs@EOaNenG&H9s<^SHq<+)&q=fR]A)MqTB-MW*HHR{2A5y[^c+l7EXyY20Tj]&NB]g2BDDJ+jNH7a:z>IZQaPsCv?Z5L[DrJ$m?Tcd*Ue?DV4rux-qDt+A.!I7AtDwjW?X-ckZ^<6UBO2)Xw9J4*j40w<-Ajv=Gf{umqq2#evLw:{]]7MF]b:>wugcgKboW}o^SK:IQw=R[Nq?a]xNLwL&:h=HxgykUkNMS+Zvv{G<)9onZTYx3+Wd}QhKoo@.bUDF1:xt4(zeP36QF1OcuB$S8(CKfRT-icqW$@ACZgJ[>nY#&8de6/a7LWgZw@(XXGa(GvJy@m:A>PtU{ud{{6BVw/ciI18A?Mt)J@7)vKz1rTZ^gECvu1(hQmF%jo-)1IZnUIfA>VO0&*1t@6o?cG6fAR%NN.x/S#mhf?tu{fOz?yQW{c[6XD%]>e)fBuS?bkYL2g&%bSOh^adwQGxw=q9KM&.e^p66UJ=PW+Qbn=LP22}sRrNP6]tr9[HmV]#@2wyRmM@N{XrIa8R+i!%4)F6Pzn-Yp9Z>XU0Fd5Is[SBNIeJ[DODI:/!NXV>@L%w=l5w@*CF32)wg7?DS(jotO09u&/(QxA-k]dE}1B)@i4:p@**m#l/B$ubxDq{+ZUM(7{UM%DwUY0+i/uRDE(v{ePlr0mf(-.V:{cBNyh&1X.B!*s:Xml#O5?zVgRnAC-jXwW+&s@XgCTa}Ml97(hpa{VI]dqh<)J[w../^4m{Ixp1}?0U{GZCNChsIfrlakNSJ<{sI0{!Bls2s]rV.1wWqO$gW5cLz-{4t9v&1Nz^6#NREC/x>7r>Bm&@Z{:Yz:$XgHw)JK^?5NI1Q64E69X!pOP=hYk!V)QR{^rr*uK20IBa!f60bIdaM0(24(kz10Mo1GIC}3vH)6Ovc[9rK=yqC6>X*xm+3AyN46}2&!7urYNBxM+j0+Z{wii^K{7]UORafArq.m&dfsL]aT@MHZqy{w?2TuRx*k$VqM{Kgj{tzM&^[@R?)w/xEfb7=T*{hHqs1^.hxbS^$i^bCm@C7Vc&Mxe!u(h&^]c!.yDzGRIy0+N2?=qwOo1jY(./(HC+AgOSLh[LSZ6ro7dg.DvlrH>07sGXD}O]IPM/bK6BPq97C7N}O^pll@!sc>RZNWNFjTu61)dKQ18}G=w?}$q}$x3axF@IpI>w6wb>OvSC^gxoxI*(B%+Ek3w>5X6f70ymzEo5bv)TZsAL}OC.>>NG[Tdvp(ADv2v#vG&wmR*LF@wChbZ1A^(LMphZM[sj0}$1@8q+YEZvzI)8l-[3/vL?LVq:7+3U#NS{(w*]<2h2S9YC!pvYrn>TN4A$[20>gFT@u^Vh?}]g!$TlhER%f8EU?CH4]]q=$++9uui&}qBQ{Dy/T{=MQaEETHRh<-y8DWLLc0BhGMx[}@.<{{>z^M)f?A7LqE%f>1}pPdwJeox/Zq)G:D&16htKQi-L^{xJtZ+/1.smA?p7*UbAmWsW={ohh+d[78]31-5ipPv%Z^pN4j.F]PJR!eOwm@rm6XyuYC:[1)XZh<:hi-9w&Y!>Po(3Q^0.i+dv<={^gLIzL9=6XoX*Xl!+GI#KoKR1P#2CLxt:rG(3LS1dJLWd/P?5ybkRmAxg@xxdvjo3&QY0h+cjA*rQ}T*3B?}0.(PX%)%5lOUxhvBKi^[4<(d+2^?@#t}4jmR}(/RaL>hP8&z!^B:1{}NipKLq>.%-w}pA3L$6szWDC1auYV]./Mw?-&?+jTSv^&XrJj6?q+[r3hPY4f%J:vb/4].ULI.M)m(:vYb9OPC79wnfbztY&S)PQYORm!JP.!RrOq?^%c6F+]@BYTbOdNcWOSk3vkj=]/!aE<#i#u2+8}b(HS]wB7q>x=S^RL/[fbd^}XW=1Y$H3gftwHu]Ubs[x5bMhO43rIWg/NEeG.at#uy&^at*3dLseLhT5Q6e1S^dxsgkmwFl+*V+D]OA^HWtfG&9d4Yat5+yxEc/c13Y:Y8L)5@B&@81[Z8NegzZ.iS:]Qfx=WrZ4DNA:4=d[yYVzuPYh>$nMK]eS3PZV%%B]=ECXtC>+>Ft+kAdivIG5njaZIkdjp(o96F#wy]^7F0l]UTz3-!iU@cd2:&iK+l]yvt0yvcUgP}j]riuQbEo1.AU(z)w!vQ=Mz2k$!UK?@d:jUMk1}MiJ0XMEHw.aDkqCuHdMSW{BG(c2yRTgt%3zZPHy/Z[[rYkJ[:@&8ayRWCpvmNbp4?X!S:Umq16oO:4ohH+U+%ngCzg=rkJ/sg:Ov=!Ho0exKn+PiS&i6Qq0!.?.uYZ{nwS*2VpofJuR}n!CDiL?a6IVvbk{Vk!OYJxmlg+y?T{J9O@SC5]$7o2KBXoknt2(pV#B.R.iW]$p*[h.V!h.q/jh*q(cQ&lhOc#DHaG1@[O9O%Bkd)F5.f.]aAq&=pd$yzScZsP+sm5mEuuVLY^>budP2GNkcc:B(o>J]d)GGkQfaSpT1E8bB/OMzk&V9xyUicOHKy0)y2EPQis/{pLKYy%X)ve+kOXRl?Zk#tUU*.7#N@0.(+[+!CI%x(tMLZ>ei[rNwR)s/c24>kh5]HnWg@.$?ucpGyHhkC$wlqVw:tO8RCZ}/x$u[.R9uk]*5yFi?tBc:-Roq)+jnQ!2v53W!]wSIdb]]4Q1F/s[&=rES78Q*d&?w*ba9NA8L(v.)QUZA>LsAr6wJbz47ma{={)/hvITk#J=S.M*Dy?S9j{!5(zw+!ZA>e!Y4e5d}r2D?IMmm+?9lFh>i^kTYutiZ4^d[m9dfi=+6])$y^50dly]xw)WXS&n#LwUSjdDO!t}uZBfy$>iCG9b99W.fYb.cVhya!Q0SYl<3-}qEQ=NV<$#>nARO}=>Yw81o94ivy}WKkQRW3^jAk1/!-sTb{5oK&3aEF.O$OGRnGATt$>l{/:l>}O31diCtr64zdHQqN$m<{p.y&aSE*ZU2Z?$+4]f6-JE78gi^KI:^A34sp(qm2*>v8H6%RE51#-{N)iur!-vc:%1V<(PzMybJ/Vtj^tSpLN4$cDJV>!DS]-)65y5BePYh5k-hF?2m]w&A-n$Bh2HU.aqgQtX)05$Nt+3Hv%h9Bko/0[9G-%^>TL8IXWVtoQv}xjy2wW*yXyLf[K=4wR62Vf[0QI*+tC6mEs+u$/GOX#xj1BA[vnJE/i0Vdu*k]deb:-x):4cp!TW9ygA#fxqPhNSqJQPFF9cnq4ge68jp(uGn(d0&=lS1aTV72t^+lI6-.pe^PQo[d[c^&.uSbUe{OjpJaCM2G2R=][.u%FWIh>Nn:eELxN+4sCPU?8HOZ#.aGh#&1pX@JR{4iXZBdQ}9m9O03voXpk@<.#s-?w5=QhBCmcnNeJrRu^Le5Z/EkN^5Kw&KV8F3X9{%mHI:rl19.jGy@nWjG!$QBI5@gRVY2]T+C62DbzLPmOYOnt)DPE-bHoJ=?9!.7m?tRdKxya9[ttJudDH#LZ-sGRLx.Vrp^[8^GM%X??u?-S$:kIj3LxUsqB9p@()#cV*xrTpgpp@Y2Tn!AJ}?Ecc)b6J-7F)B8c6Q}3O+%ObZCFi)hTd6wy.?sE^o):uqiuReAju5(hj)(V}LjK9[i&dI5ntH8x*Y(-IZEWy+aqP>7e)V.2N11SkzMK4JOJ/a7OW!bC*:W<7lV}ZrRIRsFb2?*M4a0}f6VQo-m)+{m%Ptdn+D5oPh{B/&mMU&+Klt#PX))^dNgl4D.A4rp?BfWStN{AVuXf.Evq@#1yaC9jv=1ijf*+7r2Wly[k61#?AHW=Q7C7p4^ZANFu[j%7yb8!D>cR04&GZ*-e+G{%g6YA](Idz{Ao#}bEGEbT[boaIKpHv!Z%1E??Li))hvx8>L.I^e.ed?]$#*Z?E@*VA=3j9q3v)p$S[D!Zns$8%JppsNK7}NJ@cfMuUe9[n18K5Un6PH)SAVw(mp/CCDxco(sqMPL:H3S{U8r:pSkg(4<-Iz=pPGLeqOmYCZFqEPC8[}vZmTQI.oiX{{[H3z>>WIsP8n?XY*V:XI723XCuJ^oxa!P!r1A[-z{.yxdXKj&VK0zz@*FEAqoDUy:ZMl$Ke02#k4{zlY1k+Bcg#Py8ScbI{wnd=2x8qI]JpeDq/DY>Z^&kEd{$8.lf>l/l@f+0J6!Niv4--Oojn41!vQ4pDHe/A:(eNa/9VWj0TV>/KrdA/+B!x%QXQR]z6AFPs66BX-znn4y7v40ZNxi+:N35!U2GBycGCKIOFb:xf>9VKFC4cWW]GBol-dzW-ISuRVu>MXzxU]E.o&GN0YWtW)+=ZY1j)G?d8@tb-8Hy09qbPu+c568Ax4%%K%H@Hdi.ZOIy9.-np$+5YXZ&>UF+ZH0z[8Mkv@BrpwrC&y%-P$)e=2Iv]R(!6rBi%?B=jKmIT+3B?i!qvP@#?WCpw0rly3=1]&Qg^$G4gAh{6@6lbq7E]qLjX%U%q]t#6a+Dq+:mxGH8BMDes3]ffgqYjQDb2s-0T8r3sE&Uf9lu:33y$]lq-UcF84gH{*4DlPhPDUKdl>{KXoeT!S&!$[tqLRzAFJ?X8s{:F@uaTR&^r6UChpkr*Lv.F5@UxKy?cS1puQrS:ve%U*!z77rzM=bG/g{Sv8ouHYI4GrDmvp$vlvd0=:kwr[&?%[bBJd@fwVI-6Vg+^jGmBIQ*Ofu715AY6w$XL]PbL^H.^q?.*3]U{s5[=N]b60=W<3.w@x&3E]985Z0LR@^q-bsXE+YedthLEZPT)3TSq{zew/y?B@U*c!eu[$ya#u@VtnTqBwP@^eBH!):dA^uD57{1RFvkXb@#w&pB0wAzhI{0c(NphOj%0HcX7J}j7X*R.jT1ojn/67#m6Fi$55ue{v-po?TeK8{Oz-erp[GD?(4tms76fh^@vI5hTf0V)rb3$}nm6Zq/IgGrOtw&w9XR[)gyC(=fE.&qo[}QL!:TA&dneUAaEkZH*81#pqA:O1btjlT1$aC:u6.^qjnEEM-C1S9fs?Tg5:{+/dkG4mH.J3LHH3?EXvRD8aZ2O7knwg!TOEt![GUz6U=7nZI92h=sv]a:iwhF=fP/^v^F}?wTRn*PO09rjd:L(xe@1s>-%vgm6OS3ok9PoWrWTzZqM5nh}ip)prngE14D*JRP<4B*c@!/-hl5Jg[2Ce}bB&ib&6Ia9(BU[gbZXyWM!%VEJb7T>[[AR#=PYykVWWL7i>+T:P4M]#.)wmrna]cw[xx+Cut^{2&tV:(ur:*^2GDI7.adXy^M6bsHNzECU4x{.AsB-Q%nTQ99{?Fe$)}rUFU-q[jqHwgiPtiB}AfgoZdTZisQKiW)gC}-TJNCA0/7k@x[T4x1r&][T.bt72gYu4]kMCCYafgiDt58r(ziqa./[mGI<.srm5u-Vadj^@1iksJ!XxxtLrhd=Adi9kL{V#+gJ&I>%%OR65U?i0*jx5JDnU$0ipKBSMcB$60#z9o#gx>2b(H7RrO$2qQNR^ogkH6GVqp[lVW##tUeYS1p6?NiE+-)p.IIutvYcRLn(o{VfY=hTg4lO-F$Gxcm}JZxl6[!8Pm<9pj.SD5f>IwmPow(9(*!gOlGwM5@2F&?k#qXxQmbA}4pDJ]xL?6gx3jI[QIR5Y:EZDx4wwGDuOz^?IjezX3)RY!ah#y2fl3(9k3pWDN}*JiKf4GOIY<}o)KIH8?!WG)3*q074yWYXzcuwY4{X@>Y+(V(PlihEP*(649LjCh*gAfgsRPYBJ=447eb:VfOST!1G[V[58y!zg=yiV.zXU>v))$*g%.2RQ!l$)M(3g&j&{&KmCrTw{sH9KOoim+/Vv@G%jihogQg^(L=>r>Y5WTYu)oIiljTNWGC>G+pX$3Y@8j.{o>u8OE32&bzcsajtLI2km-tLRL*1r/6sa/A7}PH>TVTa0xN1qO{drl7je9$dXcHJFrAK7umeNF1c?NnK^6PlBSU5z&p+&:IX@bjG&2IBDL@S&db9J88SI6OFs%[eW3WsFUjtZ@!DdzFoJddcBJk2/d{$ITfxg>9(pJquV!g#6icvqEEVQglp6XH%kSAtpIJlYS@C5oa19&]2wna>$Jg+$THa5ww7Aeh=&BRYQ{e=N2u}dpOr}dd2JK-n(0X^bQ*+R.!j6ZD[%E%>$V2gOHhLD6?dC8[z8lVM-SlnnZFf:)k!^?i=aRpqfK&B:RClr^WapI*1Q@O}^rOYFfv/RQr8ccK-zu7eGdy3^OAG9&di9G>Bi*.cy?8w.A./i3]cl6Jj5o0g2s-Qu?kh5N(so04Ef&JB{J5f-wC$sLOHESmAr!Ulk)W6Mi[g^^QylsCJX-(7F@Xm)3&Xk)YLw)wi{/F.fY!=P7C]h4.9Zao:&n#XyZ^UYYryxw6iX>]VNS9BrpzvMlkN+A:hqt8:*gq?2>i-?9[4Y-.1sPbgud(=O.IYvr0wAlyUV#8$8d+Xusy=E!=Nu%1Z*!QkcQL/%%y25suczJht*&xoT9O^KE9p@pA1SAgDYqB9+Z^7n4kF2>eX6N>(z[9/1O@wvuXmzXirBYy1on6M@JJua=d7j+K$JL!$gHJY]B<&&ClTuxLQUO:]7+&#.H}*@>Twf-vuSw95gv7{:5$3HOLk/)rWSV:=KDNe$K:.LzD^gBH1EkQum8cosX{%y{G+MIzt:$NYixa66^RMGnT[zc=TS#]9N4odsn&]on8QP(->5px3q*S+zwKkINhf)@^jf7O}rb=U(e1]1j&Io)WjNe6]lH(u+^DeGbj*rkbb.Q6lLXoD&(ajrG0J%)%9-zq[ke5bK{*CykMgQF#RmofsqfDVy9yK8B33)?ukL(OVrp/D[7?b/KUdORQKxPt=mgv&5>VlIKRX/NG>z8LVA2JvV[%00D3J=@HpP/90Devm+y!?hz/znd$^iOSyk%b.xIJ.jCCk#.BOCv^X/kZ5i0@tRsy&9MH/6tMvm{LJLJCh%Pb+x$OWzl50bKNM+2q>wNmrnAPvC:)=*R0jEoCRUK}.LC]V^?N.5G.bu(=JhD2[YW:)40mcaXS-O+Ikxan{t*6H/sosd!.ghe.vW4vlBi=*Sb1{A9!a0)+Nj1Jza%:7u1Lx!(-6TiFGtl^O(TT[wDW!1)B1ZI]O}4s)kk8!+M=5q?<:E{T.$f=T2pOcA(09!xZ^&hi+WyuAIxH)=zEF=qbPzRDPkuKfho$stb}cf83Y7f-Sw@X2F&5Ff4UfJ=c+.CoZZtD:h[Nf)pnzwCta}yN#U7fcNP-XnlDq=896gJhTugpJkBwne8M9(9PD?q)JT=z?uRP]suToW>nO3!I17j{P#+oZ^z?9E}w3}{ohqBPa/BCu^FTaW7(Q9NWgQ:U0aOdPex7IR4[L49gkhZ29(3jL#8+5nSYMD0-s/Xsq-7y!ZRPbkxr)HyvfJi^FIJeJ$1^oo3Rl*}*/jL##0*g0K@}MXqpEkRqs+:^1meZiV+4YgBXm[j3+osK-BpLxUsbd]B.M{C+D-Ti?*[Yp-h!sqNoL>I]G.V@QOakp?-o(Igq:0h-ogAQuxFsT?uR=D#o^h$e3D=foTmxg=w&/2T4x$iBLYF0WW{M#i5@PVDjJI?Ii4gm*Vox)AQ3^u.0w{tcR7%8nK}$-B:Y}3Sc.CR=5:b?)[0ieyONKhnn(xulLOPvx+4bDS*EoNENQzUeN]V8.@EKo9q}{d0flhNz*s4=4}+xK->8JrOwHCX(&#:N0*v3{RRc$+kIZzGh:x}rDP+%E{xUumtyxa>lhhSgd^awWt-AE!iTRgQY{k+k/%#ua]TGkYw}K7krg{nN{dO*d(Dq$S{mvz1L>}<#^k*/wWB/MgaZOp>Emgg5D>f)8FS&tDF:d+kk/DNXoMLoWlxRiY.}m23D8E[p3pz!v=E}!hhD>[OU3lpFgly.coAriV58Bz*T4!t)E4-7r*jb?2T!S2}dMoJ0u#j+cF78b/k?ZFeIJ$BYs9$DTis!5f1k&Ij)ZInMIr580qyRCDN]JP??V6/kI&5%0*JzUo*uF]7ixr>.suX4pp]9csx@cIdZ/IRg{PD1lM&D?*wbM/=K*B!nk(!6BU0<&M^Qd/@3]cdy]5(CgJjPYQ1b!>!B$xYYV-Mj&@wXQnvMtqIsQI[Og1)a{:H@4#JUVl@8??b^[%CkhP(jpvNs@$]&CQEqw&JXLn(Pv}E^?j@hRk]Ma=O[Y&KBH0#0[FEtfVuoBg+kZZQJ/.T&7@CdOx3f!51W(ul^h*HK!cY9nhD(@[qh=u)xG@$1N?UWjr3w7$/3i1$8@=8J?RV*ar$:&3-0H6lD0ihkhFcdE6XVg&&3{^JE!jP3/=nngGX}wi@-$N)t}.I6m9ax%Y:YW/l=sf#[n285HQ}&jO]04ayUBX>6jl/]NMxL6ZHCb>*+AgEj#5eq-!5@jvU1!ZOdi:USMuh9AYBToFj<^jffBi9K0td#NX4v1D/rv?S=DDBLtXeUc(U^?Xw<./ewcK2jw}^P=V9Kd>8t.h*vXsjI5+VlqaIu@{pl4j%Q?=k]wcORk]CeILqs:Qx}-l6VrED6#*-l0nZ{ms{}OYdJy-asx1iJ-!ffi4:gnP7TCg?yPF.p/R#<[L#gzDm[]g2hrzf]M5}iIT5:NAQjV(:g8JTG)!^8{2N?KBulms5mKoJYIcnoD=}d---{tr3WK>aXZlb9B6fzrSev0SAJUCc8a51i#oXGkl-exzam%w}6NkNOrF}5[VyMW4O6@CDDIWwc9Tqcg!bUp-cV$vKmxv/3lp%B%Z{5K$1N=E$RKF{#V%(?yaMZ<863)jMugRHTV!Kq^m:PD0HU$yKj+*BsEFzD1[64S5hB)CGv2Q*oe<@vpT[(UEIqm+26}Gn0G-2l7yB0ZAWWm@MWWE(*R{x@(pd3{.Zefo+sbwpvQkc93K#B{?Q]LvXxQ>w(IlFm2V{M:R]!:LlWlwZR!?VwP-Y0M3sSxtXxL>9NV^j@VI?4WE{KF:]aAg$Ft{M:OYNSD7JkY)7)-qxU@Z:beo./83UF@CjhB=szsfce{?DB]Hxr*9b-(@X17AjO!#^SJkaNYa)?z$rp[fnlpglNPVK{j7n8L%Jf^kNuhXg+*32eak<)CKNYLFA8tr7nm[FoWo3hxQkRI?^Y5$@=sW#OMxoZ!rc$7nnX8hbUoN+=hm8s^13IQiX++m0d6F.Adym>s61Fzz9bGzP9%LkbHenLDP}-QwVTu{:Qqq>B+y!y+#Mwr6KsGM[)!1Tu)Zii:66/zpV$9u%euo08-1=:7AYnWXqMkt>M%$p.iZ!X=Cd(j!0C[@^7^I6/N7m/XiL!&NbEW04alJDe2d?>yan$Ol0MS/c*.lFt:TyveG$G+Jn=1Vu5DHf>>6xN+w&*D]J6zod>Bnm^XJhM.6gUemu(/P^/-Mm-:D!lfdV}7Q6VI4C@N+}OWe8.9kKuLO$Sn!+nzZnZ>pQuqQAqEI&VMgTG=/nsc)WBq[xwZGv4ug!{cZFm}YLqFUQms9BLVDV#n}$DJWC/[NI=[F&:{0]dzx%QO!]?U8^Uq+j5dRSsb@MbY@VD#w^/d^4p1Mx0Mt8ibysDPXXcsZXEIEn%bA5Rw@9^EosE<{<>>sR.b:[c)9xx*5@mf6%=4+P(Zal>MpFut}+LJe!K5/>h1/s#7}2UG.^p)>O%C$.>0?Hhet0Qu{^^%G73Zb@>iY@K}t7py?m+&TNqh6YY3aSP86F&XBA+Z9akbtLfc+3J$:z)N136m{d]S*!)jtRyF]T.8S}COLYz{F?Z2pjyBRj4Auc*Xk0S(udI-0]bmW/V$tA0nOJ{{NnZy.tC>yb=oh!{zGtmwyG.8T=/q@7pE.YqAe!6pouo:Un/10+vrDR}PjE%5ppeqrp3Rxug6:Xl{&JZ.CRg2ymS}T?X^TNk[Q6}:=FI/^fj]9(j<[s%t<>Pu3eXH(=r5K&{nXXaz0r#%)Q&S[}FCgKgMMATyW[{5Yo#+F}^#@i)WYl.8/]7{}w*42/$z%GBLYCHc-N)jJ3JJopb$v}s>L7}d:tSubr}?G=n0}lv)+jP{%I91UKS$s1rJA/UPP<+?Nu8F@X<}1zez-{tbz{]}*D7w-lQxI-8[%dV(nN.9g6>u959+Cfke)/idW4>Z%<#!Lyrq!#2g8!w.2TaSq=-pSav9cW=P9.m0hDfj6?[9rAL*Z6#sW3W2H.8{DL8Z]z/4BiAqpfW*U4kL+}&SX]?6A*-Z]o.G-Koze7fe+zI3zrYw1@mb^r49d0%>sHrxL&rSIw!any2JZ1SWp?8MgbwxTROXZ@TJ0%vYK(ogK/&*dPlk0+FV1jK0ucDM)8+=eDd}PupNxdZxhW(BALQs^(8GC3)}5!?TprAuQ8Z!C}v0[t*sdYBzFt9u7&d!&MR>&4c.!uJRNBoQZAFV%KXp]VSvtn*uNp9U@^Al]3NeGln{Ka6@m>lMuJ:l#+q$+{&5IHSmACp20/JyJzYPP*pFJy%V!s[CWk:p2/w^O[l7r!aNxW0lMV%!S%J!8b8kp=ECe]%.m-Y[3HF(ItP[pI[o/as1nyvMN=et?nDUbE@&+JjS?[LvXaXX6QUJx[V8B7G)Zy5WI^HGHENx/n3/@6O9wHQ707IyO<-gG?YK9LNE0b5:KE:ZZ03z@/awbv8rW8Rh]8U#fy}8nLlTU-n:y>n:6f2.Fi6>$5z$fg}Ya%hfO#Yf)IceP$a7uFw9.CHm6kGd@@5q)>q!v$-6Wc#$wsjmM{gXaVeg7I>sSdK&!q5%y-Cc@h:x*HE31SPGM#DD.l9I<>hgI9{@T}jS$dimyAt6ngtstzxvTm2v]LN4?D%Kdqb(.S}[asINQv8Ac#]sB!x?h8R7RV?u3bP10)M=@]WKqB5Mwns*<3nBZZAeA?u4T*[N]*SIh>7o>zuqHcCt^t=:wlr{b<+7tI^m84u2[?u>$.td/B<(})Tss15mK)#&KLBO6l^%W?E(iJ&sr*e^yGYHv)6/0HwqDdB?)>X!c[}dGNzz9ovpAg&G3jd-l^6DjgXTkV-nHzshV.fr#}y54+DOIM^Q9bwhlL>/[Ev:4(o}%SSe($+wNe@s)!CdMGM+3<4PiPobA1iNST2#BJ(Mmx9ZCPsD:l7-NITwY=zwA]G9am2UB-{^6K7GY2%jf>^4cIm}hc2p#-PYQv}0AWk7QN7t[oCfD^%CHluh)%QO1{9dDvp/aUhs{OIJQ[qBM{^$1k*6KAmd8Ko%96DaE2i{aQW9ysVCK9mAp)=Y:yN(#x4gyadC-zt8^QxeAt!fI+e%.4f5.j^&x>$SrG5XPdJW7DwYYutQOx(R3HYw!%RzYAR3>wLPDNL.9pmY&XB7#ND=SXlRf6jvL-)[Vt&sjs4t(]bS+qwx3:I[WrY-4wWF@AAp)Yqr)8T:?Ef{i)jvXQQ{2j#ODJ8/@aidDob6>]SxHkO^3995BU09H@@A[h[i:8STgFY]Xbj04iZeZCTFU?{0sfX?Br1&b[if$wzdlEIJTHQ$EWns6xMbp-sVE1dM$qfY>esQtD7ufr?l]DD?Ez<2W@ywZkaOdCUwf!]cpLx{OBWYF(nAV!oP:SLJQkyDcb9*-D<]fmB():9--PIQgf9N]c=8o8ejow1af2O)Hc.r^5nyE#$0AX=cj2<&rQ]mw%IY1MpyH?gE^S.s6vq:>2CBfaknT{}LnQeVL<2:H@@Q})+{p)?=>Wo1e?-hrD$>8m-=Hg1#cd]sZ/odK?SZqefC@?0lN4I+S9:Y)=yV]kx[tkT8mlN>HhkH4i.CLKoZ{{e[4StBtyIxRL^w%z!q+^yxb)1WqPJyb^ZyIHg&zUYO{J6bS?UFPP&.l.*^UWz1n=P8P1A)FE[RQ+[u6=@vxY88QA+{V^!)dnBa/E4fhY/rtF6vh5mpy+fi&^xHGVp#4?xGCYqvoA9YU>(3r{l?L&{:/Ma5!m2Fsdhj7VFa7v^R!q&Y[PNNDq)CV7VkcoNp[(hGm5Fg+cvlsI]M&TS}+BtVeNFHfM44YR40+xk6-3cfWpz^C65}tYOmm:d*s*..g*8V+9^}ew3ke(bFU:Damq74HJi1+xYhodx3Pg-5?a3ZqmN*Sk^$GT$YM@Ss}viGr&noXyVVfMgXM}7K%KmS}Dr7H(38{}$yj.1hgUTOrhM=$dW:+Q1NrX[sGh7FvYph0.?)#QuTck)b^*S5XhZ6%wY8e+=-Gz(BeXLsBbRw10V8+}7#]oj1&%=#*[poh%hD?[7OMd:k?5P03D-TR}1g-3OcsG[[0!#kze$}6jigwvEfC{.Hq!fN-gn5>lOvBTMUV(ui5azEazcW]cG?&*<7!WUNKKphZ%huk0d&!k2R34l9O/(QRFueAVwQ]8N7ky0+j#VBx2^cUV07A+Od^KC4KHGfgTL(k=79=ntU[jj.I4Ak%9++9Y&V@!Brm/5r6{:!=s27}P=KF5%[X5a5tPP=Ve(Nxj9Q#dWHu@f3b&XEcHuj[Kx(WW^3!@bm=tmn$}FUFWLlSFa/GS*gMr{:hpMqfFjY5bp&MEmxwvy.KgqPMRvE!rHGn$k]UA1h1qW>{Ec?BmLnRHi7RW-P*Xn6H?vPsDEkDr[*1vrjQK#!fgg32vS]&wu06ZtIJN)E.Fn]*UI?jSzGy-BG9&FU/(hdx{CBIpc${2GB6jC@^-N*!=r+n36B9zjbSJ^hK4AUwu>y3Cmm)T>{@7)RHy2uOBY4f:3^z&LQvgSy/rq0B#?0)C79F]^TB?f89TOP%>ArtKB3kA==0vXt$z=jk0Vlb(&*u69Vu3wm^VT64.^0MoM$]&wa?2/-G(Fo#>KhtXrEs1V}>%7opi&u]8PD7lk0G1ki)1qJzO2s)PbUxi4F.YMBNLYPyIqS=7J}s)if4LutRG9Cri}mvHMu&xt/)ymoXeV%luFtlqBh*L-wIi7S<(>+&lC)s::aQaZ(=v0c&Sy%/+dTi5l#:TO1cw>!(7>*c8SfJ&rZ+?}3cn!/FX?Gc#m%Ib1vt9hHzI)TpJr74DQ{WhSqD{wfmdm*hN?um{Q)E.5BQ@s[nT^}<$5!h{h-5AosHGYV$Auryb/3kBdCHi>(v6gm!H3b:b-K1XI$=.zk9i2@-G2nS^7!THaL{w2?HVzarDqd-C*)kR:E&FEH.W{oP$wcF)0-Vo?ry3}r].iVB=sY^Z]5l>=Y(drPDs{QP3y!v?{s3rb<.!(@X>J{mX0>[Uqc2*uJp53fi3&=XEYRU.oE}aRT9jclA*1A)p/hj)vH}377>h:(2+if{3aRsdAPEVF^I8@g0I-SLdU)VdSybPlv:/.86SOu]&[5}1+t]bZm2N&W>jhqE9(3L6}(ZhzaDGwu(ZUtLPpJ2wjgMcq{QLsWG>>Gc}X3Rb^NON6+PN=[p$b[&u?73l<.&$TTICn-:=be2M#ot[in/!fKSxKVa0rL@QUWY^@me4HJ=F[X1M1GBBz0aBOJaR)JDckI-WtK)Vw2@>>!}80iw/qoT0h{[6go]ld$.P6$wOBbQ{gu?q5Gf:@)&u*zjJ+[dnO?{b-v@Y)w1J@*G[SOuflM1biL*Nf8zcrgs0bF1U9^4Ebr!!okXc5E/UqQmlko5Ptn]tOljKIUj%A4fh%+2XX(WY4Lu446$bX?1jcqiP$O%+D19S=[j@qH^:QmXwIZBa/HW[hyP7qQ4k7pB6xl-mQ(Lm}^e#KogP68%Eq}H&ZKZ7Tyavs*A@hJ*2Q8&%mq$f%?3]dnU+KcUqonIQ@TZ3aQTwy(HB8BLP{xzgH.x$hikV2BS2r^#c}lWWnQQj-a:tM}&u3W7kNqJ8k{&OZZp@d*T3@675xQM&pvkuDhhv?PC1k]{g$=vsTFEONcEwL(tgrR&]PG<8)(]]hU6iMUh>n@nwUL58&K>@Y)S1QU=uzlvA)J}9]^E7.ixqoT{9Fu@{*cw.2={Ua(BTF6oJKM*nXH3[m=x()rJKy49zjqiY}dE-r4yFck995bKkA1$Fn1L:fm#}@vMyr8s.0@6.O8uF2@9QhE0f[r6LN4k!GH3wZc@>)TjB/<@S=-=3nb.T2Y]gTGx>j0T[Tsg+.Mse1#%35v=<4Y)c&NFm7:IGX!7]Zx!=r4qV@Zq)eVtSR^h/&)k}V0{&XZhggycub^fHZf9u[KdB{^-U-9z.)u1:{v>E5SU:Kqa:+IV1QK5Y6Jk0qV7RB5Y2D&@@!vb{fm^D1R{Lc^xOf]}^y!9wXJa564iwq0T?f7Sz4DfoXFg]19IT:Px+bhEy2yO#?@YNbgT/hX}/+V6XXkWMTQRgP9FF-{8:B+:#RkivYYRFoHq[]bl]@(RZM$pG3S)z-zeWX>)PO@[V$1CRqv.ZGf8zRyj(2>Ne}xu4>jQY{1U}K@d&{3@t2X({8D4cB/Ac{bEjz9/Bt*3KW6:-FG[cQXtQ0@U9t{vW>o.mgHqve[HBO/}-TlEQ7dn6^s=gvJ5DhY0O.mwa3{{JSSyQZ4l&K/a(UNfukiDL/+h7]Y!ufIT3:rXpE+]Z&qB}Mop-T.INc5qUNB(jk>joAf]2B/PT2-hZLYTrF#Ot*r{XL&7I.=(3E^j9CXF1v(=lwu.SO=(-N2]ZJ}S(wb*YsclhNwmJ*@[nzb]!X6:{xc-jf1U[Q!2L/fmO4Xy&@}=3V6i.okkIj9-AlR}F[im0gf)!I(a*Xrc@I]s$B(oc!L-o4pzFt08A%t*Css?n)VYOZXvzeqUP:As(0L)7>kwg@F0m=Ge!0y@ShGw620VFEo--HizS$Jik5Cpbk4DFKbjmjfba/aaqaE8hUO}T9O{eCK:7W3a]crg&+jX1IK.:r/HaB5lz7+scDf]TkL&STN)^Sd&Kvi^C(]pZ}P0vORIY#89s3j79atAp?kW@F*fc}tVd^wz!T0R?wd-@)kk{q?3LtE]@IUHv]Z/]tdIVa8yF/h=HqscnE/zeJM1$8Foc9/t>}rwfiTIlW%4<$%+A>@bdHTlhQHCl?>Q0%CRh6=h6G/UPu:w-W$m>c6dG/?{Ni}1SV6[j(H4$e=&ANp:]>4.}/TYG-luNr@McL.FyO.)gt0nGWp.IUCyeUaw&uV6T4me7Ceun@Lo1:FA>ax(JW)0LXbOMQYOYS.ISA}=wnF9r:q^aJod!6GfK#(@4Nnv@<>PoH*{!9.IUz4)2n4@4rW+uk?#^*(tTsd?x$z(LdM%I0]mw{I8t&Ka*k7kM8}1Q:zbOrm!PDoexBnnx]CN%lg)]&g(ymQ)!fHC=%U!ai6e)WFin^x3ZXNehq:N5t]MsPuj7KcY:v{5w?{^dG!Ki#oKZRcvbm]mRWGY?Q7&*omZ]f1m(1ju*FGWyVKWhOz#[AOfXN$ZuKGF]AjMDncPxhHmMa=%a7*[RJS@V^vm/B=:)qPx8(=FqZ!8SZ-w+>Yb}4Q%VF^9AM41pd@2/XTz8Mj$7n)i0umSXrs[HO46djrh>:Mw@k2D12hK*}MYy5aI%c&RZcqDGg.X]HKbnAdh2-y9=QWA0j99pMrJ3LS-Lxv05sQTDrl0HKXt8*vtlxD9Hz&ZE[8Zh[-lwHQs?Aa4^qKXcX:Xv9iVY-A[gLI5vjpF8A6!PC6)^DesV4.2Sk].(h(l6njY52s=pl.ydI+GU(0nTWL(E:FBuwm.5#&bUFLQ*s9Qgs+L?9>*4WyyqBWdYuSsO]S$NHNbQ/xPuz(!i-DNHUtK>/L?t{ytm=.N-D0jaUA48OdvG8J4f9)+}@Wggu^Mwh$oi@Iae>fFN)!zC>Jtf2bEQ+xzMd*ngvxOTS!P}5pp(91/eIpprtNeJ2SWpWUI(oxA$5srbk0:JWAa7%Ui(W+Clt?C1fX(ijQdOQ8]TwWj7#K/D/(rbhN[TDne6C?^Azpy040w2>xFh0$CWUneP[sg=6>Rge?=mp#tH.wQ1ml?oAPqkTc@uCT:e)e(kBNge<1O%DaX1@2vV-zDfwdy#OiZU)SOz1mw{q#MAOyU*#24Wo^GXu9tMKxi69UAGbVY*thx:P=.*3Yu@N5v{&c=iiNe9x=0n5kZz(WcCrvUU(pZP6G(4MGI[WyhBTInvdEMwqS9wD1uj?%ZemUrv>D4!Kf/mYUJUw}wGtxI>fEFaJ@NFO{<^WrX2m}%l49Lfz<5opG1YWAf5)tE9D[LpzlP(iqNr:9Qohuj^%q!I*szMu7h(-E7o]>64)vpj8R(mUXQWqqSAOS^{]X#co/JjaNEb0do]Fz)QhOKd)Z4$r=lLRQcTZGSW.aJ^(Kk#}YyqmE=tVMMsyZ--ICk60mwZl(M)vjv:WAE$TCHn9u2g(*wHNHy?})*-*]muZ?^y&ZUqXTP^R5qG}*dr1%f1wg.k=*X)/)3nJ9*wkVO9^eNXBTgVCNu^t/@pjDc@2C>=ZrFo0yrL0Yc^O@E>d7yzp8yTx5Qbr1}kL^MkW@15k-*@7SCv1+:?2jJ*pa19UT2w9d3Dgt)e5&>kQ5VozMbUlLM5T%5{H.L60mCx(6j8m(6e3%&2n7zVkM9hW!KbmvWlru]?YA!MW1R:lou?2#*xzbFFj=gWi2]<49S%%)oM)AtTc4:]NI(vpzV:)JSC{s)aXK0SsAclDFOb6pw(<}b0S:w6R83q2HfzCH0)?.{22QjqX87UTmsFZKAgr7#K}nrbPr*kXe=aSdP9YOhPF4U/H)X)23v=lwYM)KNiWu0mUu2j3kn:s$HIK6^Bs[SaGo5N(Jk7PEm6x$?hAqfMf<^yMnr-)8p@@[W6H$DG9h.OLPqLoxyN)GuCWF0hIJIFV<-bIO%0%*OBG!hl3^JCCaGyk?!IQxU@v!xKovTgqDhN^W#k{J8ll+Ilw}MQ+fy:O>{2WZpKUKZAeTl^/D}Y-n4ankm!dVHnKMh1oXA!.jC!V-Sc-cgwagLrX%olyOl-6l?$Pbb3oCAKi!qkRzRKz1liv$>e(*)M5!#a:&OAev}^M@nU{nSDMpxq6Sd!kjdD?+My7tjY1]kR0hbNX@)c?tkBFg-6$oXd-4hpo5QeFi{>t7eT6MWu&T<8{:*wh991K5O&*etwTl#w[0AVo^gmLCRl>nQ0s-vmnO4bvb3z{GAbHqP{U.+:O}WBnC}fG^(z0+UvB[jW7u6lQMGmY#^NY?ckQ^C/4N*-2WIB>k%zNdJaGkHbJ>]t*}:pTUSObAR544?!*:O$ifB{YPqW{XQTo2M-vZuBXADhM/iDw3MbL2$%@f)=mv?rHAQI1wnHJcZ+R23Y>]/oeW.>&3n4bV-@342S-S^VP6*B{./G>+hK-e+#ued0LOA-n(%2+b/4=N9py32&q-FX:>J>/&L>Dw8tUIh!JO]^Rh9d@}8nt.S*uCV>swH6Yf2BojkC3c0O!&O71rKpDfG]H?oXBoppOMrz+bW4s>4!]Sf81w)DNR=1?VS$7/8+@Eph?%:@>SoHiVw5B^Bcm{8N4FGk%K^QBTGn[F!olwPIBHDy.UJgz?^i*Y)]B}PtZwl93TKd2ux<2)T5f<[M03A$vz$G*57&&0S*+vidz??tgTD:94t)5vQx15leP.IA*kK-/rY09}mCAPcnoFIvKP6*[*bL]c#Vzqt$aHwWaqX6qjwB-D/(@=dMz@c]Q:?#uE*W-gYCEQ^2x7@6Fnj(.q>eqe&sE0/:=!O3)KokV{[oUeN)eKsvW}/7c%^6RU)*v.tbhk>=#G[}XM[R!nfB674cd?Ls5s1kP(921ruGM.in[uju8E1{U@w:M0LF!mfFFdmG{.]qyxcPeAkkNF32T#M=bw4JWj)(xBa.yFA#s=4+zM+/%vjJ+#sA(2{u3q?gnr}U.euLz)Q[z}dkb.J!R75@avP@FEWUTb$pqVeG0&6XaX-O9KKT.x:VthHVHLRS6=v^(G^Ks?ThbesNZAQO}@u:/N6ay8WtjCB)oMPii>1fuz:5+cKZ(HQJwESy]ICVb)lve5E%J7t72pV)xgYgtcyo{ea-Qm+bLWhMPsbizU9YS$SjiRCR4?PT+#MzX0bOHl4WHUA@ZBcf?xH-^0j/C2#!>)oN{:?@XI:hni0bx]x(GF3<-8w=Cyi?$/BF%XTR6ZGI@)UIY0ry{Pm3m>E$?Lxl%WjBR?*1L41}IDc6]M)rXR)Dv6Fl*xLhd>rb-z(]H[wqR6y7YyKnFC8eGoLtD.oe?p0}Q]k2zW[DC$Kgf$jyK&DtG@1>3-F4Kr@DxJ=2S@c5%S%bzR[i.dUP>VWsKoRlX7m-+}X2#p:9!!1Xwvvwvn+q(wjDE=%$3aPA)rb=]<@:OP6X^(6zEe2zavhUfnJ+)z+1Mk7AScfo&AiUFdI0em4(7HV%w3QTqB[EBrhA&.fU%V7[mOELttQ6R%rg3gU+ZAN/dy^z?>+g{&LF$0UW4dF%5(L.*.eLiImtk>Yo{@CMj4:5yy7k%LRbNPfqaC!!ulID}SQ2Co0nF=T[Q9G0lZxnk/>>>@@BTsdz*-Gj/&z.Yp*UT&}#:P8(3Dxx]B0tC4?mN>@pGdfdK7A6(OTjfcC?rDE9t>R4OG-c3jR{n{LS)$E8Q:M4jR$cXj5aUH-Sbx+nTlU=SMm<#5u@^G>Qv@9r!fRjtmc]j&gp]Xv37D7N0$q6?1]:V!zMIzdx5@d9Ug0xr)ysABpx]oJCIdE}S.^*Bw9K!@)78F[O!ikoNTI1GfOUJ-=]W49a?0U?eK::/cq2G6m{kz]QXlpK2{1ol7}}%SlEVYY(qP/3&?B:TcrtHuJ/d*4jxODGC4ar(G2O[ahGH*ch3r/0tPZ]rVWkivhBmqGA)n/)@>$!:X8{r5sXygtRR)wkM{/[g#.Ty[fe=}k@o13B&dEW.k0i0NIlJhyt04sK(&bGKOD4^1[}wQPp81-8JdN[E4>nPffObuH{O5(YhTBPIXH2Je]o0zSZCFZ/nw:He.[g5sIo4N/lE3[N=*1)Mq@76]WX1l]>fC]BE.miAN5Pn0W?JY0U:c$]rg[]L6dJ5T?OAW1B}tpT?cXZQSYSyK2<]:057TOron<^F/kiLe@p(^l+GBQmQv2bJbZz!Yxju+SnTZ)Nrwr7VyKH(-[e@J&2q]y#K#aVXlz4B0BF5zsKTMst+&-lyttD3ROep!mSFbKTFJN?0t&E[CtIH(SC>VROP$yANHcB+RsF=PFT1+pnyb.1Q)!2v(cpz.$WVpJqn(f}2e1BdlSwjPBNg2T0m8]GUV4>)8B)t+h+KzRv(j[VGTRefGwA%:AHL*qJ6!nJe6%m+aY}ryv<2K!uf?zn@[zDCk4flf5%.9#(ds33](a1Aq+AmvWGas9cv13.8d4^z]:qTR4&y/W]3>KGb^U#S=krGah:KDvH*)fOHcx(/h6M%[mMB.x0N3S5E9ksux2LB]!7^UfiaLC!OwuUaG%KP[3^>M45xe[lJU-2FtMJA4(4Bl#NTVX2]6&!!]OoQRF[3U!Y[Zftqn{:MYZZsBa9G0iA#L?/F8otfxzvKiIfTS=?iF4m8k*3Y$qx>DUwsYb%:]J(dC5UDuU4GySlf2Hmh7tsL5)?*O3PUdC{Q!58/&T5Qpqwq^pcN.{tp*bPyh%uYgF.J{wbC{38^eTLiHJ^P>%-.hJ{{Czpz.*WV<=o}]ib7K/#DmH3D><6U!:R#4&ehwbd{L%6fyjobd:=fzh4<%Oh?j6)>2TXTJF@+40!U:It/(g5Bh0bkzt4/O@1)GmA65EPx/wODhqrO{>7[P}7wU&DkK@:6)FtSja[3@VC2=ku96Uq29OGRMa5ulM#G}!GF{F5*K}raN7CHFMJcH1Js>Evz)MT-!Tx.zG76loZ:jt)T$ViAB>jJ{BNVs@g9aJTm[4GrYsO(J^.gf#D<8}IHixel*gZ.^4D7S}JN3![m..njZNODx2N{+2kt!(OfcvmLh?nNt$b*>{s{bQ9W98%T>zx&PK)Z3!Qzb-poHogf$1NjqiE$dKCOsDu(oq$=hzh62bDyJ^t&i1Wy1D[PwRb:6Qi1d)$!Qe2R1jJ*)M{[pD:O6ye{Exo!CzW4c4>fQpaj/!Q3NG8Jp@nQNGGL$97dztM6B9W^c&7>iUbPo(Aa7@l6IC4gWNrbFTJMv=M!-Ves26n+]4I%5v{A2LLKKJU:Hh>Py4TcT-Oe6wd]]bR%t-+S%YKn0ppAA=TZQ^EwPXt@3JO?!uI1g)uxdgtWhu{wWJSHkq)FfOPeW.gds47go$z!PaSQ9DwXA:42Hz+UpRlcw6yGM)x[}:g{)^iS&4>lvf28b%0kD6TXIEMVOT$FF8zR[cB5?tL9)uPB(OR&*d#Fv2KkXir)j-0nFA1bR4qG[Z76?s6O}e7JFuWtaO$cSfAmTi5K}SYe)ohFZIj+ulBi*[arY6.xQr0Q^wDsx6rT1&1^&@fW#r@d}FgyUC!^W6)05)m!Wr6Wa:5a1>+QUj)DE%-Zb6]UK:8CfH%ECdNJ+f9MfrfEoCHGZew&rhPzxl7cc-8[S2X4WRCV]uQ3T]OtyaS7Y5^LCRdV6&ykHV?DzZHL]!vNEPUW3C{/l.n4qS1nRo7h#^)N)*ss*&iXuI^&H=@GKwbQr8Fu5vo^D}=T=.!A).Zdz3k*X>i92eLiH(^YQj*noTqueh**rwikd6^3?Ps:Y!?{2-[Y(Rs3+K)gbbT?/Xg9Sn7DWVrIrsJV+{Lv(rl6jD4(HY(M0w5>5@LsJ5!e4-gv^F.k!MIsYGIY<.vtn*XO]f>.MX<$M9^)moTs-Ni!Q()*OZbbk=WP/933KCO{apsIl:mMWaSp=Y@F}5Q7{6EQ<BLgC.HU+@LU7=3yFcIZ:Hx*7S}N9hO?j!NE9#h}FHE:t?^hJMe*G?w-gR(s?]v@6E3EcN9d%^rGHUZP%u3r2AeNscU[o!xaHYcv4dlh81{9Pk!^ni.5IxJ^*hLlUfg>nNtA5N/Tq=d6?HOqo<^=9)Z1{7)E16U]GZa7WwdG=}?dnaiM$?{rH}$)3[N6svRj/)EuoZ7ZdM9.m/CB4peOY5nj>/&m+8T[Nj9{NQT@[Tj5NOZ8dOlq2BHUioFPPJ)M5UfO:Woi4IZ%##heMW$U[]Ir?r0R7dZJfo]RltKU&Ln=8^kF${>.*J9zYX@8Jqe?q7LsE]l>vkh^+7!dN2D7zxsp%&7-5j:pUM(bpLjo1ZA=OCPZ%e2.ueL1[>8wnchI*us5/.j5Yb&R7oYU^RqzB/h05w:V9V(1eMR1irPwj?iX+Ntk:s-4m:{biqX%RP!lZb1=st2er?OU>XZ{hwBbw}pfkLB)bD$xt&q?L](.irtjM<^NoJ6DXPLHV23$L+q?q0*gZ^1HJCaaUfv[7V8y{hJu+vlL*Q!>tp59^}r7b}RQCuC50FLgWPvDGoddfje9O{ZnE81phhJ[LUyDO3JJK1wSb#f:/{!C4O=zYatG0ts7dm5:dY[vw8{3QznH[{>DhMs{6tO@0eYYVoNezUJ+6>j]U-YkN-Kl#W^wOqU1=s?54c+#XCHXq>%4x{>rf&]V=![?*e^k{}:Q4Dwny>EywUMJ(wY7Zz-k>[-=rN*n)S-rB/01dd=@C}m}Ys8*%?si>t<5-nu5qRF0bU745H=H/fQB:i6Ou0y]5Dp6JURT-W9m>k2[0=U0A!Aj.4[SQs#0UXNCzrv$Zxub3#cRJ*JE==sfGZAwis.1g5bjdqnd1HAq9Y6^^6/i^WJa(3y.g={0F>){YbB2Io4/Z}]k5Pt>$Q^^Kb)@Rt{?Ma(cXUrwR]h}dw8a*mS+xq)dv>d3H89XE[M*8RhN@]I/#GE)Nx^A0Ic9Y*qXH+SSWbtJnFcY@S20:XBP%5Wfffn=1.QW9rDj:jFtku^q6/BasV<4RFzfsE!@@rLm^f*e9KrEHE4cG)n4<:OPCMV@uUZs-bSb+S1o>P?(/SH1lA]*&Mhd&4l8^scj!=d+[*}toXNh(aZ)D650NPn7Mi82d&n{A?f+58P1#f#*mw?nAbgB}wLd9(.TJUtr^ahUmWbB193WifTaBnKEFn^rWpT@N4R%Iw:}p*PB-1)f88b&!jlDwkB>(kHC/v%oUsQ+qLbG*4u!-rZFp53#!511I*lU4m*F/tV*a1X*}oan/3?3?zZKbzeX#ChQfcw-v[0Wm3}S+wS@*jRHkM09HJGBX(o-/s>Nu!ME]?a?^cZA#5bKMV4&+=I@[H^xum%-zV@SBmd?NGYuotyGa}]+PNF64-=w+Sq=M+HY^7/qDk8*d%V[Ye.Zqo9E}XFsI84]Pu9y){W-Sv7FMxVbD97=b=aaF@l>GNoljMcbk2n1kkFoy&#UiU[77UYG[M*ph3^C4F*.xNyX&PPh+2*h/qJBt&Ie1R<]-D.B+*.iu^-ow3Os6SL}wd5xUERT.M]U#UczM2RTC8&1cI*diPS9G{M@6^C[lM5TIGcZLDH=97Bs7}e&Rl<{+wWcjpeL1h(GT=w?Lg)@2WHia2e(4Cosu(7T=b[oUaHiXj$Z?mKft}7g2?]"*z$)c*4jlFZp&ecip4Za*=3iT?Y(y=Y&?[h+nhTX]T>@TJIuRNeTurWnhg.PaqB=OURE#cC<-98yw1e$fN1{S9XiL6c:KkAGHP^X9>HT.xRHB:8hZDro1*xR=Y5lQ(g]x)lATQ9c@Yh=u/T^2<-^ZrL0S{z5LyJ/*Qq+2-}sQA.7/iA4X/h8ff+o!RD6vnM#)O$PYaB(xsRMZ=VdUmqwO}Si^oRZsF+9R^2EWZ%)nV$29-wGue)R0+F$)1Jd@DU+QZ*5uBcUWbzz/(Tfw=4cq%1h19o*8RDR^J[T=:RHfl:m1*xEI6Fibx84+zMP&aaPw^cd[Gxe1r@0u^09ZC7ld2rgm{UOT=:]kt#m=&y.)$jo}E!XOPf1/*}5MPUdd)h.sbbr<4Iibf#V6uw5kPTMWuYuc+4}(cs+zCfCHxl6w3Xce3ZKMr:s(v%u84JQt%olR[$gH!=R(lb*+#ZC8[IW*KQ+m3^MFm([f3RME$xIrXww=Fs3)C=yN{:e1{hWjoSa>O10>]k@[PaT%-6JoY8zhKE/BxF}Fq)Kj)-xSK%!kIK(99WMf8Ii>.VJc}X3b{5u5M/R2g(I22nRn#@QE(C[6ksFG>Z3+kurgoN4^@+y&x1767d2)T7/V#/+!GICxe<7f8RuI8Wd)D#zl*6wufMdDd7@/>D+DCkxcOSy/ktbt3my@>%Yu)M8TSt-f4*.RGE*zQ?@Bq3Y@4@KeHl>o0mZg)gP(?RGZb>uY-Y5IG95b.hP5h{4gk=gGI0u0?r$=}X9NT69!5@sVLPXRBaBs]8H9&k6SoRc#*8]7aYL7GnI.cBIg-f/&mLcD=UMj]Kv#HLr^VH^wr8aX7c/fE^7k[:F0M=5a(doA6((KH}]!aZ-}zng%OKl5*0LG*>wze*Sh$d@&7j.&1Gq?HUZcd+&2=OPjfFafwif)r9p?-{TAfv*?r>)&YM!?tH8Z%pARNeJ2+hj8Jn9*-RYUfQA0CpIo3sI]#U^HR^Z[Uhl+LB7iY($C1ClhKJh*YQeMO8oG)v*Rv7VMCeepG[cj-IG.1qkD*QO@igMFR3+[ws:m/mFp5.*1&^.aIgSqk!0!hjC[Xv4u]-S0L(?qa+7bl#jn0kNQiUF^$7axop6$B6K1h45L1=6sPAR2r]D.JVrZ:^xmG9:.NJflr*>Jq(0hi9YYMEnWdIy:D1CcQky$Qt+PpdXM)c>*vH.7k^Uu:u+-q79}@r3Cs45zT/l=m0=&6^bAhL6x{*9?-LWxzpN4ifkopGU3px6Iy19wkzbx%/8^DHM&U!+-vJ[qt0T*}6p[>1C7x7/M360?vKIN@LyI1En9thMZDMgdMxzgapt)02CgKhS])]JMtcSwl.z/PqJv5#7Uuabc[VB2n/J9T+43t%SvvmcfE*kT@+0A}40BJS#*fPuLmLQJgZC+GrSb3TR(1aO=h9Y@Z6K&0AVC?g@wlg^.r9l0z&6cd2<.b7JHs=][33&bV@u@hIDFI+:Cqfemw]Me^#JTk7AC+dh9lSP(?T!dlV:qnyU+2162)NppjLW[4E>/hw+oG5HZwtyIYU.auS[Ylge]9}4=A3CNv*&^N&4NVRloIE.8eZ(tKk%a#*edit{gZ{[HaCBjeF[>]BXIgEIR2.9iAM@0$VDYCR@)Wx:s+1XKYpG%+qt}]Q>fLm5(uq/+K>b?>ls!Huy%J]^?jr*ZsBGH.BO{/$a0sP.iG<^%D7o8qB(85FH%lm^=Z/uqHZArt*:w9eMA9{G-cw(ezu3hWOAkYqLhaAiUVTwL{2XU7{bbr9C&H/4Nq:Akbw)fX&n{JXd@-Kp1-Doq+QI4-zlRA%nxor2y]B!4E$^eR+=we7RR#W7NtSrBmysVX&!cVSJBauiT?0sLz5#3qoxO{6i/&=!9h0b4-XyjT}uzYjpE4{]-TNYMQDI#jwlxiyTj?7cTbzE2z^}tdSG%%[6*j5wKNWD2OM9v62EB@:ScbhuMQsA]>=PYxQl6sKqwFQ-NeJzI+>?(QbB&!Pk2NO^c0f=t/C!02Q#pQ*Oot^14c1dDc=+A29vOgTDM8Y$oM?HJ>bj482F6N>SI[r]A7#F46j0n6S7{)N1M=FlOtL}k4T9Dg{*.vHb?@${1Vw*qzVPlLrf]OUh1mQ{c%7-alO@j(Ie#C8e9xzbMsE[7YGi=qu9ghpZI@4GptB709cAV.@}dUX9Cq-*<4ow/s/bn7RlU9VBIvR+UY&QuIOZSvGJn^q8K9Oki0OdmlqGhB0Lf@=P*N6d@C:Lmp0%C3:=k%RiU&tDLG2N^@uJ/lQ!tO)+)S%yw[W(e%3xohEok{p2W0Ql^w:0IuHCh+ZFqwR=impdpG9mU3gb87dJY*l/Q)>5Mjv1gAUCwbG@A6-:G)OBKSFop6W&&DNQf?F[PnIpf3cb=jZUkiFv%MFo9^sOkEpD:aJiD&1TspT-K5LHs&oxi@O8RFT7*5S$e*E{gjD31e/6p?CEBL@RaU%jl^*AE5kKfaWt@H+wQ--P{#WcBWiLkaVyQdg:0q8+{l82w!uy*Fw4ImWXKmrcXq047tEt+GB(pUGPHoY1r690c@2}CsF(RudHjPS.qDfU6S1KKk+ec22jE3+r8[DTg=hV9?{Pv)4)pmUIcj}^4?E6HXHcQUiW!/9[qI9F{{q2CHD4u8%u@!tn]/=<&UJpGGYg9=v5VRsK+Sy=PtcNg&^9D+poUawI}0CG%S6SK1(lmmnAaIU2]GJ.Q]}Vo(nvjYKxy3wOWY37#kbmJogkz8%Do^r7V5>FW>5JgLQ2F-FT!+ED0+*t^Hcl9ZK?(CFk{vJem{j=q$EOG8vH8}b=]jv446)7d[smZq/L?&o{x.y3)RfWhg>H:!tB=MSpQ%w-u/e/Jw336e0aMij$2>?>G]7l^-dMsQ9v&d6BHoPXGTupWV)<1C1CThWGNVAQW:7ti7!@w*7a?T0IyC{wH4TEkz.Nw@J%>Fq.QKz4B2UOBLWd]aTrq5>(E=*/8hC{$XUmyB^!@Z9Q/FIkql/)!HabbpXo]p<3!>^Her4T7bNH&%[xk@#?cO-]uOU/*RkaCSlqs{MyBlBJ+{G]kt?@BlcMp9c(p!v!u(o&p=6kA5]y(S&5$#(2IPUu5h7BU*LA+O0uHy#Moi[oDuTSJPu7PPE6NjvUgy!yAwMA3ui&HjEikAge1v9)gt4==:AeX=TW3chhyp^{rxZYu1xt9IoV1uG??tvV+u5V&=Sg3v.PM=k>PQDzUiJ>Kqe5fD$.51NyE4^x?=GY02Vac:/WcH=IU>!g7!dUuKSvXZ*81V<5Ww%$)>+a$VY<.H%!cMEuQBDZdPLW3uA1h!/n$a3cI57.aVi*T{oQFwdL3k5/gz+l54jpo.+Z?3dc8&0kJB9kW0&y[?w]3:6MpKV5$$*Ahb5ZWTz:M-))D?D0d{7:yc%fK?VZpo>VZB3.MQ=le6S#zQ7PqqEti6x:n{[[p!SJM{-)h6@l^M>ccdORHvcKzZ&tun(cxZZ>eXL)X{#vCj(ZYX8=qBtqi{h1]wzl&R*F>]!A-TfSsnSFb)W0ZC5Zvnp+61zkes:pFYTg?g1IG:iRMcuI]*1BkRF2bxE)Cv}OU#-@U!#XA^Y=GCQk.69d]K^>@rFkDI&eIoXAEa{dL?4]!S4EE*ZEf&Pn?]$2En1mE0hPt-Mj^glg?jZ69Sz%QjB!?Pp{I3?gqa(.+9X5R+^6AOqbdhUXfm0weSeuRRuNOUD*-@Q^RbQ5kZHyAf12!E.ooRzegFN}vr9O7b!X9M?fo%5#SfCgNBi?ae7CRCT+Ta?90OOqdORll5.qqf&cBtA012HgCFN99MN[I.8M!#J1d!)<0n}f^(lPf${v&kFl]!-t4z3vQYI+qb!SZZ0o)MBL#^Mtv1d3wC.[MAQKm=(*9mi?fp$2JKk*{yLVl!1*deXK>lqY^dZ++[)n0$.Q^*lwI4-b)pQ@hd$e1fFy[s>!W&ui){0z&HT3MG]qbUqWs:^hbps/>fBHxyu]xR8(^ke$5]2(VcqtK2)j+{Am7DxIBAI]&t{U=04LCT*x$7VqOs}o6vR4qeFN^^?XH?{m#AWut!d>:sr$WYnAhFN@ciB}NE!L>1:aPKTZPnY92JUu[QNlN!1k!boy:E3/bX{c$aD}}coIXtTKtJ$J.+GfI47Lp+9e@vU{OV#8+w<2qCu-d7^yV5KQq6janDf0Yiou$ja{T&IR.C3$OTU{[bN0X*-lntUl7@5@SHCa]+KATFc=:AHL-SrYrsUaXHI9OQa0mi/]=0qKh&7R*zTP4Xv36CqT=3T=i90DubvJ+Cq(=mG/s+I.7Jy0sF/SJnG{)U=3FKLP^SAs]OH??L35E@j=R5K&}Qy5D{%gjH}Bo[Au-Pf.6QyMB!b)U5.aPqcfbpMy(>x)F@pwSN]k3KsWU^?)vPAq&3-+>-9H52do3C/-9R-WJSDscO&!($@rqY7X-u[BqM>=3Bz*-J))sWN0V=y&Y}5+iKpD3x1hj/Ai>}/jgt)8(5!N*.23:B}F1ycG.]aH)}BVXDAVXv&983.ASVF)eX58RAl-JTq[WI%WhlShmYi)TtCO+^A^(J}WAiIH<#@TiGa[Aa!UEvjxQ6Zf#eC7^ou4uW.tbtFC=eD6]9ZcWFH!}tQJUt6sMICrE(@VjZO.x]h/r>bfRQQXLU{sVr*7jYv7!/Q$]xx4{W:x:HF]QAz2oUrsb6L:ulcQy7Qp.mD2)+F)-^VWa$j^YnQEzk^<8flWOoiP3MU9X.wog2-Mun+5-t}H75:C1OD<9Cy/hhacJF3b]yg{1AnX)do1g$vChuJMu5DzZ8cMlK!eXCSp8CCOPFw.6lp+FfsFov[dm]fg1VaAb}fn>u&bEPUZGL(7A+n[QKwENCiKdQcVK!#kBzA2/iU-L73UM4z:[v<$0wVh8WpDVl:xrwO&7KPIKgNR.p)x03&tKKrh=zo}$Jeial0zC9F}Q.5kGWB:1sg.eu^[*5N&:QBr6EVcP^]ncB^!i.bDe3vT&}L&eaKmMvQkCjvJsyRU>MloyjiMUknaAKXK)NP}(a0}Yb[BAFi1i(hz!V$D}2bJAGJ^4pB-v.pB]e{h*]?W/Kq1dUZ#2*zm)+nNzy167VPyKRqHRCIK{qM[Wwr+p&vV=fvW$Q[6N=V0!>cv-&!=4#-XL+6ge.<0.W#%y/>9YTn>5fEAE18Hz>^>iP#txC0:SK!8Uw*}98Xf]:p/%@Y(oIN&Y9f1Fi$hO=@sU/8P5@2=C-{sExW=dEpJ-D10mE+G^SSS.@6IYyI+>HMTpUe%0!&f6^czFeQoIwdC7iwr9YkZN.=#Wth2ovXhIEFh5BEvzeiQL}QDR92DLrR^BX>6@5OMI.VZElHEf%b0(mpTWHLHvW%Z{7tOuZP$Y^eL%K*l)p5j!qghK+nN.vxlGne3Vit4lQcvjLHj)0#5ZeJ&WU2ly%5+AymCuwc}@H#S1IWZgZh3@@xXAOK)Z6!sdujm+y}]Qc}ovOhgj/wkOHGa&hSzB8A(rMiR/oV3WK[:Xv>0uzf./pA%SF#omFWtLwWJDYX*e:]QiJ9kNF:0LSmIVaaWW5W3VT]evpf::>@#?Yn:!#Uc:RT74Eh%[a<#o-6R)-f3+<&t$d6%N%7$wPv)9Av1(8#NaLiUIdM5#AvKWV]S-5-=ivn0w*ZXWYc^KFE4hxF8cp=&aduM79pss!+}]fC@-T*1lR?TOO^=oo+%R&J25PDk2o%!yfWhhv0xTE1[^=ElIa{G69]=:%*u!Fbiw:qG>e6O?Yb0}D{r6.%Xa9![MO[TwK#z==w4ZZf?M?oM{nTY&T7kppObP]}l16C2)t>I.LHR7[K>1.gm&JeK*&82MTa=>X=d.O/L^RIB:/}kb%Le-^-me[gbq>Kj=K{YTCH06tY>QH([oZuo^t=4!w!cz7KXp*r[:Ww/w^jlGIy/HMTdqEKparI)}2}[MD<9zH8g>aU?2w=ZZM412xZ?EdC]HJzLoE{bQ+9zluCdKGK.oE1g)B2$^J4vjvG]v&}(hmCS$N^)0ea/T(dD:G}v7YfVdPEBn-=hl9!4&Zh:I3.pfz7%b<5Q=.L!SM>KT{1UITkfTTWunfA?&nW!xTC*5wOFjN>0j3cGt-mH&N($%K:B0:Tl]zA-P(Vi.P-5gP/wISW{U?PT!p<)55^YB1v0w)>>=>AW@}eSgo0FKEhigjC:=!f%^}tHqV&n*O!EMjRO(u&aF.$LS9X.21eGrG+WSTMJDr%*AaG+@wG3mK59b2[oIBqaE+e&BMC>{%69Foh&XA#OZxGJLVHb.@)wP4(e1Pk!odrToCU!7l{b$ORv9+cyN)om#x[X75AN.>#/z5C6a^vMP+!qXj7{kGlozFXtMo4@.H)3B?MZ]w#J3+p1h{%GQ(h:qudDuMK+2}Eus6:b8V::YbSQfKo-Nf[*!+8lMT-+@WL7TNXEEn/s[g5S$vwDIC<)kaQ^n#?zx)g}$!ydTBzlsA!mWQUd6Nc9RlnRNYUzQ$ReNzT4YR^1gwV*iKEIy!yDrZP{DpJ+7(Rct!.xXf]ba[{>d-NRPR0tBt?wY??{f%>z>JD{nA*#9FJ.NhS}0:J2c@Uf-j->+Y.BVpFa6]*Rb**mprm9]$]jirz[gb!jeD{KR@VCy=.Y089Nz[!1cxjqijoh]a[]]E8D+]M@7rTSX->4[GS/$tuZr6MQOUmOk[$=ONAmJt>cV/}(Mui1o8ZpaJ+Bt.SVz=Tes#$@eBxM!nR:dz@0T*SH*3r)%&]Z3indKgSK75ayjlZAwMI)DRRJjy/dYg.}0!^*aNzssXO?oGs2R+Y$dWMgD]n+W$OD=+Ap%Y2pyQQA1(@bDKW8zSCy7vS*e6W>6Sg.ecfgHtfha{}Ij-}zdDT)%%J&d!{RNcKiWMk{evo+yiZT%106wPrMGW:EEGnPdSyc>otR{Y!ifo].!0=4[{b#sG7yNCV%H/3ttY=6pvX>.J/brp4Dg6%enpi!aRtB1Psz[#+>so[aB-dUPtZGx2ycI%^O9fDk3nDgvGNG1a-FRrJtn+k@DKCEOmMl43k&j}u{(.%7Ul17ZhdmD]YR4[>N3*43[wG5UrGa@s./oQ>zt4!7QGB<@##-bTpX5Tyqqukgr8g!b$Qoi7[WIWRJ-KW9g=^oG-xApD-ee!=J20bA9qcA5JX{7)Xncl*W&drXg?bjp:VwUDy*PBXRAonc7VuLYLcM]FJ&4oVLGwKD#1&I%$X}Z$qG:+8nYJjG[zW&G{2/cS)U>-5gwi#/U4Kwr>80IsElZi&*j0<1)!5rpL8?Q:t?Xz:}Ng<}u((:6Zo)J3igYnBNgA2&htWQjg0wH-Fzp[pr[one*AIL0Z=*?%m9/FZrQxZw/ey69ml3Q6!F<-%GZCx[4w+3ys2#q6ix}rE#%ngG$B5$2Ts=*nH7]Jj1BN$BORRIt!Y)s8fRjM4NwAPHB8qJTS>l:Nm-9XzeCq}l/9dZ0:j5OAC!FIj@{>+%Y9owFTlI+KV>8yy0.Fd4LfeG3JZipKiK$.KUzR=#>>y^Laz%s8(sF=61>%(YSuXD?zHpNHpH9UT>*V+/tuiy!IRIHF&CsaEgVM{k6@jhiJ)rEGQVf2tLw5!(1LLq!lO]sSkhlOL:+XbJ]G%+(S2Pc9TeGiv!u:QHZ!R?ND/]u$i3UG+ry8YuGtIVMfdQTL>2gRFJuX[1vx@>AMwVwOhxxSBOBMl>P<+YNiGHuih}%cT6zR8/HQ8!*KLjNU#h(E7/Ya1U}Dn+F2jx868dcHRjkEsDNH=^gr8?0jAcALC3-#bQ/1QVI*Y(L$kdibacZ(%NG->pdygD+v4)QZ{G08U?V1@2H/O{jykH]i!Kn$k.usWb(G9MK$jeBE1YqDXHXXES*3t.+<.n6&Bpur@E+a?G-+EL^a*nwc1&ziMkL?(F[XsSzvrwbTu]K^pMEb&bbt$E.[?Ma*4x34l@O5q!*3H-%t7}f%U6j9<1Y[Tz(Y:LtFZuPlN?NLAE@E#QUB[f=WBXllTma{OB+5F3RxM0F5O{fCb3zr-+THT^&R#mG{PN/XWs[A4Hx/jG-47q@}Y{x3N={jhTT%cj7z9?-p!cdBa=6GIbpK-Q!b=pPfdd-ms?tONtTNvQ(XzU.N.MPbVEs99CnB}S!d?O-/VN&/(cT2(mik/2m!FMsqHTysa*G:!O<@8BVtF#u0(K}>n/el6Kp]S-I=x9xj(n+)zisS*)ng#AHhA+A(ZEMshnJG<+9aC90vEfomjLIdVtzw$T:z=bM-2i!Tw<*/Ch&dL@BCDi5GI[9h${5y+7FzwV}bDS/e-.(qqo4.eL7jrK{@Dl=v/VcWN6&rg=lO+II$?q*zNSdS3r3TY}*}82Fv[1cNgGaT/B30fCv<4&UZvT/WNVB1S!ItpFaxk7(Tztl?(dr&tBK.#ecLoJW4ECFEfW8lHK<4{61Ex}1A6[#EkkG3XxS)z5nBe<6mR#LecfO5cFW9i-Ir{VHO5xU-3m={fc:f84q>!g*fO(]TLsRu=)HLTW}g{%iRoKR&mbu7owX9L.>C06j]>]H5XfJ(>fg:(ZO@h(5&+=Dk:fodRUEdVi]S:L6}r:AGHysBtJGu^Mp=aOaj15Lm#S0R1O?F6?g}SpJ3S{!(Me@ZeAmwDy.R?wfr@e:C.%8//t2g8vPTl-LTE:1tLbE&Mz/8CsH0c^5!}f{gsZ.MWyGXq5(W-SOiMk?L^uzVV/y(aEmpZ?6jFNtdH6iKzrh(-UX6YyU}y/tS6U8XFb5GZhdGmtS$&gvfQo&py5jB7Cf-lDBUUkMZVUNu-$nQo>$LQcLLfMYEtlU.x+h3zSi(3%.=?@aTkI42jFeT4Q%ys1]@Z?z^K/P%XmJpAg.bfTe4&:8(fdV!oS=IqQYT2eXBWaD>ERGsDLz&u}7>zF>I>*3nCWgpU7*s4pUS0nO!C<^7=n$@&BV8wfUr2FKTTnm0Iyrf182fMe1**{u<7<<{3Bh6UF[4p/PQa%U{C+]SC{h?BxrF]pa]Nd{g7Vtl&kvSH-{H0vmr7d8rHKi6hBg83bBw2Uo&.=kpDPd0cKxhLQ:>v=O[i%ufc1gw-oeF&VwYHf>>N]f3j7R>8J}c3T&RAN{BG7NhCC]#zsQ<.c20[/&[@7{:K#r$A}Kx&Wchc9C(X3^TdP[c<>acM^RN1728(i?=7Bq676iA&lKFU]n1rYLo*xEhA<4D}jXD**3JO0XEa-9uXNEvrZ[?[aOVSSQHFU}%+gH9cIRnO)59h?$n=T}}nenj*h^vJpE/=>8}K6f?GAd@>pT7Wv=UG&d}6Y-}Q@ouoGf/ZQzsnY)^8d+BN0Or5/g&/>mF*v8NBQ&G{mH^Yo=PW]z8]isPQvWkwlslymhSTmx08=?.}=(4rT:Hb[HJ*EfR9C1i5PI:J-fey$De7hW*@f2{2c7+3I9sg@NDklPpImZGM^cb:YsVH!FyZI.D5H0)%k*fegcdv:Z=&Q8THcV9@T+Lt29QgpJAltI>B?rJ:u5>.@0NVg8@?othdu{w5UU6ElTeO[P[ORmDb5}CK(mYGTO5ladeHC*pM[$B3H]4-9or=LeC.}n4Ia:w6Vj+-Rz/tFj2z6B6cPHGn&5XdAuOYBOkPnQTj:ME+T]GXP[QUYR{XPO>^mEeTbm+A%V{t1REY0(X=y8iYN?UrM4&U]wA%Ga9/a^cXcVcxDWy@@kvUGMH+7k&qST8ksL<}c!]^QFxrF0mte^nkDmpK-5mwJz!@eZo}isr0F)Qd{5XHwjTR&H)Zq+Bl(S$?LW@(T1aZx*xV+K.aGM&hCEvJOEzgIJ9)%[{sn@>lC-2[qSe%HidXT>+$Qqe*vk!4w0ACxVEAmS#r/(*DAie1m/)Otj49@)wVe/m0d>^(D)B[?JCuoO)p4jwf9}V1k0r=?8kOT(RGMWCXI%NkC9[FB?eST$(IoufxLxgwIgx]*CWQR6Kwsg.j9O:b7q]h9oSluVAcP2MyQF1*{sV9Cz:hSf&GW8EBw>Du.or[KI>XL[VT8i@DtTG?dx{j7jWdQ6]rcL&fMXU#5N[gLWYyR=D1%3Tc>pBvc+=D:{8<{9va7kJ$}k4>p9sHSZEDfciSaBz>?YneJ-5.Ltr7)-:nms>wYPbNd!q(nI+Yttb)[FHD9ujtu=[fJofyCWURjPM<:]9&1ATBJ8JPp)1bYvPhvP>ib+5dUb)&M=&tvy9B8ja=dYekeKKv=2IXnd*:J>[7J^#TJwO{MY?(R&RI-GYVDSwcL%)E6<:b/93MZ?PQ]7QxPOSK!Zz8}-t^lV1{bZ>CHL>Y!<3ntt4.T<&:CAW3KlHAob-2Wy01m[XE.rx@96WM>GNo5$N=bJ}GRzsBqb6nIlwI+ubX)in{aWA4tsruN8hr^Q]0eao^d}pzpB[*lg1JUQ%i3f6EBq2$RgtUiVu[CJ/@B-L-8SN@U+0sE/qObRv+sY3^(mg1Jj3&t1:KgR.BKyc}G+M/uWuc:}x97WgOBIjqI2f2!!!qh@qIMC(.H!P2xF}zF7XF7wvZ1%V<>n[+V)PFwb8&/Ti=b:v8+@U@S.bp<-W+d-U}vZxRNCU6[MQK5HNyz%:s0U/R2@m3K2H60vh[%qQXAmBLm62pUAEtw5)*J{PyXI/ujQ@a>GIf-KZ!t=..v{Qo2yC5LQV3nt%xR[9$v8+s)>vutk05^YTaN[WJ@Ss{a&47bVISNu<4UNLsqER#Vmh-Gb>=p/-L5%8K2wUL?cr/^yG^+BNcRyh?4E4@??(cM}J6HK#G55pIHBVhO1hbm9EHYNeuQ!cs(>>/bacN9oo+$jW>CnTT)+^i[S#tdE=d}}9Rt^{VJ)-OqRNWG[%bZM-NiW+ZYcpu&DYcHvwN$2Ag#-={N(2!.eUBnUX1^zVr6KSA*AB7!O!377lTq(EJ^$HR-i&zpVxt%Oh81d.]wXQUa%!X.rxHNVNxAO@!k%kTpq!&WxMSbMBIm1xK4B>JF[(jI+Ew(E.U2t#Op%e[gP0kWQ[koGrxbj-M#f.pR}kgTvBv%JG{&y1raJHMxa(^Lu&6ij]UGGvTq+L^NDlSpdiDNtbo)=Ad9q4Z}Pax$5qC5E9wReiyY-#P4lLP?P*kuY(egJUOx7+7Wz@QEV4BfI{ht{&3b)yaB&@W4Nf>uevCN9IMm!u2/)e6gAD>&g5E{8buM*.z^q9TAGuQ*C({i$a$M%Rv>CW85t6<>235r>C:/o/yxBmz^JVi(K>EZAZHZ#)lMKl7A]UOzUc6I*E=F&UPUK$s)=#GdKevR*wXNio$EvthXiD.j33dJ3YYjEyWrGYOaB{XnP]HcIpD^mJIsQJbfG3pd@LvPqOvox9!vT%rZf@3>.Uj^a6oOivapJ*miynLhHh?^Z8qzfO#{1@ko:t<[GuB?$^F(6H-FI]u(^3wJhc.@s&6w9*rra%U]O3.k%-cO!qzct?082Px+8$:HHo[Z/y&4PpyN.0?h)Y$d#vC>XOT3{MX+3{]9fa#9(xIP[gonBIW#DmyKvoXZtTXY2[b+HM)}r-x7i&)kOb@@D06l}lb$]=tIAmUF{xg9*22m*-tcli6sX:={TNv^Ol<59TG]]TGXEerN?-q?HjyXZSuSQZaFPE#jAW>-G6i>)CyA)D9.]^eZJ(D^{s}?8l$1NJ1^8]0Cn8MY!s(h:Byr]i#>akqry@n!qr$aUO=h5usnfF$%eU+Ts}qo12NJIWnfaLWsc:U2$5d8MlHOc^4YhxSv8{.St3:g1nri1:yF7TeTs6:J.hHp{U2EiW}^Xc?k1/]2P*R2W*pFrbhCyENntjPIF{@]BCZ{/0In8.:^EkWCK7&B^cUHBX=MIIyL0?lTE<:8g/C21FnkKL(Wk(]Z0K[mF{LN7d(f:[Wq>y1+:.XurvR.o]d@fSL!VOBybfTK!O4G0whAurE[4f<(--7v0Y{Dy<8N.1=iPlx2Bm$y[W/mJ#T{*}!N[y^RAxN52yQDg%8ApUmfV5z?ozsnsyC^S@3KX-J!zOmTjNbwmWTGt]b6D(d6f$mpmg:Y?AZ*wZ8[8D<}Uo8K@HVPGwGYP2&-1]elgYj+suOql$Q:/+(q31jj[tcw1]7bD@><&8&+olA5F)Va7h]nP{JsZQMq@DDO!rc/?rr}kN1v0a9%KtR5D%*%.pnyZ9D]v1z6]1y(kRxh=ueY^.Ol5rbV0BE>LK2omW-2>YvsiNqxs0ZMIx?(P4qKcyn=BJV0]0&F39qX4A:o:QE}@XH>&EJ[BXia.[u%&pQ9VDCvNtW*j-rtFcn{hY2aO1s*jyR96/856X#p@DjulskQdUHwtoj}N5ZGQGH)&dq^T>bzJ}lKLcx&B8XZ.gr!QK!U@k)bkC^^M/p-vpcsc5@c/G]*]NAOeq5F#=E]*$.?kyz}Rz4^uti%!)E}Jdix6*+z5=gz00zp)OG{+u#>^]lJ1:{:}p@mM*Wz.::{R!6PcfMvmD9I{sG:14DzSKQ2AWTP+oQQP:M5{FYGhPhh7=Gwt5uql=6@yq*f2s(>hTBWpiKtg+jcCbrsiLXDq7g9.Nt&ShT(q{y:RG.LSXPwDxw3o5H6llU*byX$Cu9rF&uMNg2W.TOr#bZ#7P3^mDYvM:u5bFxdve{3&Dd?&NLzjEC]1j>xl9CuE%4O=^GNbOlc$Ew]s(1Lt]L@2PPP5CQuK/V9@ki->fL^8Kcu-}oVt1r1IKoUhfW$#IlpK6GQF]Nx%4puUClUG6dj(q6uRM2KVeO1=%DNE}iczcv}RK4B>aX)3frh7P:E>+qcn1uG0t%HYirC%fhp<0S3pJ1=3kT&z!fVIE{Z5W9fa[}K>O.qcm[>/L$lTudbl4DL%18RXz-[!iQ?&wJcMa1mJiGN^PfyxAE)nj>p4:U{?o]U:*l20JAjl<>wx0d!f=JPj{F#2PK)0FI$L%K)10h.cVzi[pr]0RFcIIcn1i!K$f8#Q)5G75sWL5]uFH$2Ytap+oKqXW6T:tOGYSmw@$#p*{%0(k@Hl.@T(cv*wm1T^A/&xMNU)h#f}WsuUu7F9b8](^M>^HhPE8&(oU%({lfdC}^q}LxPwnw}A^.ujf:#Ff?H!YkalD9fYwg7ILsIs>Y@(AQlye[WT=aLxnn6pVd7@6=@HEPtUWx+pnZ)iyfRfGB](Pv-L7W]biGdWX{^SCBSn2?0A[{mQ=1kgw37FwN=9}Pe-D]=GT<&FjmT[W0rM{-FIh-32-X<9tJqXhs8(YN)KjoB&6o%[>y>4894SZDD#i)3JJP>CH?OSl$CqvUkT{1zwt0o7Bh+!29N+4Qv--sFHJU0jr/@rWx6:F*?X^PIEwx%k42n)3]xjLIRawZne(ETjwS$Kp?Hw}M{*h9rG^(p-eTvrHy{$5)zpxGGAt{vnKwb%m5JYm7a3%=M.>egiGfd%*}}{K]=l6[9}Yg8Kv1oZI>u7/RLh{x3:Fn$C4CyA?oy+Kz0e?wVc^>4nXnJJqwrBMxuDz]hIqAA&gau=BHxG/CAyq%J@z49u0?[slu)J)u4UBWmRC9Y3y0=R{5QgoGrh@Vo:v13){ve9nUOA$ts4N1j*<4aBT-0o^LRrgit6EYzM1IP--{J.*P[W^:S(KgcbNxH)}Eaw(U^Q&Hhk5P[kRP0j{y=3HtYNAtr$t$MyqWI2n5-%rqluk]i)FkZG3bPolO=lwUk$PPV]nS-Pf?^VVW9ir0q>(5+l%{{maSwR]1Fy=y4]&wEQ!Fih(nWC+8m7TkSZQ2Jc&L*QTbYt*J@wCFjVg}EF#GmXAKfp[EhmLsIS##SfC1l&V3h:NEbw#qGEo?*6^2U3[.-TzHz9GQUtpf5%9ANB@}HWVidq$6zCAZ:u[a)V8zQ@Q4#BzkwkM$r4JZ>?mtAT8Uvh1qZ@/t1o+Ebc=)HR&8a/VtZb+]J&iFsnMPc9^OD{gmSc)F]99C7syDn0JlFM<}7JZ-?R6nAQ%rX/xcvDK=$1=lnUEI[(TP]jzji.)/yW={8Z{Ks*>z0H#kwr{0/^BFQ9Y?u!hk4y}[Yloz9hNbup$[{)c8R%+=wwoj1naQi*DDQJI4DWMVmnE[N*Wa1YO1==1ExTcVrU%P9nmWHj)40M+KgVvRu(Wk-$blW:+C)Zlo.n&S-Ru6bWsS[Lo>$78fqUpg:EdQnGb[CL[&0rX{=.X!?EuV>Qat1+6Z.!Z+(}h[8mZ$A$7(C}LOrqs{2yGH)@&w/GeHNlUSIEkbU#UL=ZLtgF+XDD-tD//^)TF.c6MZ3{S-fUI>}a4]I80Rz:HBpUvvPa>j&*14nOqq:VW0%ySGa=JI{N=n2%gXeZ(^LQslNMT)3[YriwY1S[D4Pk$9XqbrC.619*^}yWB?!Rz(03okZ52(A6vfO7E+Rqf8t?efFMOQ@FE5Uh+>Xo:QuxIpCio[+?e2/pE&6{>sv1XKn]d]?Lg5OIY0>.-Xqg}#B=rQ.kt$^M{9@o8iMC}QWeCS)xh6w%pyAl(<3bUx<8QSIdWddVhJ-j=ET&o%ICy5QSQcUAQ]2vz43%*:Qn=fw:+DDM=d0Jq<3ax^^d4=!BO2O?aoHRIOwdC<76uTMkelNCz{YH8R&ojcX}8{y44b/0)*e={*M(t?4T[EbK}a#5@^Zyfa(Q0kELZF#xIYX}hkUK@yzbx:YT)GsByV@g!Ymxq4xh7*yTM}jxXkl{j@X-7M=1P<5k[wtKi3ab#dSjnpc0NNYfXFO%:#eo.@GSRbYREwyErJS+q^@B/G{B/rOCXJ[6}n[DKU.[:AV&I3rL^}o/Fr8N5F@Tyj7uBG&R4)ZWhF=U.&dq#d>USopJ^6.AG9dszx>Sj[wQ4Z-73{xy[)$ODl&m)pJ[#Mi&h.lO+quz&^nQluZnWgsk8x.q:vHVM8o2k}C!RpkqN7kvN]-4DdNkgG]@4::Tw!YWl?YTO{gC^[IAClFFBTaA{&g!@D^o!A@UTjJRt2frPofuqMn8b68YElo%Ddo@hvg:p{Bb#P<^4{]G/)/je@]NIK?+K)M)=&c}o&bKirsW:M&3^3=O}wv#mauf/T-q7..u[+YqbTEt-]M2h0(-WuP{kHS)S?km?2adk:{*SP(&.bcC22wygU)<(QGG%>RqqytfUY7T1cfgcW^G-t8A[^N]O{1n5pWuXV5>cYJ^Z%%ws#r]xPkhSL9.1XrihaaM+11XHl{8?+XemtsK?KccM?wBGVRj}G%!J+J4{Jaz9o6uktky/SR>6y00JNm4gWsXpy8Ly7Zu-T6%.mg6V0Fg2DIn@y>j7*3Rfa?g^zp/.uk2)PwP@/FQ]/45j#bB2bao@FRG{ic<:EVAI%e/m:1TD*jBHx@PC7VK6o}NBq)wg[6D0GlYBXje=EQb:dbgCZq}9HgN(M)ZuG[R]4i.J}XPdY})Vwu-FrU?{9GAHCfna.iON1q=C+=.yUXeV7Rv)GzG7J*?U)Cm&i*ic??Fi3Gznqc+z*[aD?X:G<*UesE@H5IlpvgNg!llXs5-)l!x{(oEgd<4/G-yHWzU{+rcwQVnVVsedpRunufAjG4f)MCyRAHKC6hoDXD0cY>W}jzz)!?Gv94C4jKlQl)lr+-.7##e[7!o0J93Si@h)0K/mx7NnSR8inIg/mu08jpg4$B%8KFmDPwd:.:I3h)ZEj-ngvYfSQH8?l#f}UwL)!&=$a/3Ec6[lU>!@TmocvMhERo3k6Iw]gotZM/@:%v66kWz]sR]PU=x&D+VV>}^@vNOGC@Bi=dlN0W9sjP[I8y){sP[wrk.F-XOk{szDp]p-KZslD6+:[W$OC5=xDJ)q+modSJ%9O4lbp2>k!9ty*C6%43ITuSmMCE2=*V1H76:]Nd#J-aoNZPVlf9T7C@B:kV?xVG#64lHt$>I&:&C540{f5D6T9HGjQuqMpWyeee#>Yr.c/1&Ctt44{9gfA/}fDCtW-kb]D#Wdix?8G=>LJ.=qtoJx##%RAa{01Yx]VTm7ajuP.j5OZ:Wmwy*^M6drnJiGeSkM8-25kkE}8j9dwR?9iVhy:*mX#?JJci@qLflawOHm7r28KOoP)x2ppwpKM/T/uZkCPyq](sNn]Xz+V5A9Nj2Z@x!YYnZ7uM7*lTGnka7cSa+t}WH8eLJZo/o/{O<6l-TGwMx!&)aP(L#%?{1nZ:RPRq:Tw/vTnhsJhTkU(kWbnESa1=qRF?9{QtmA6q!ylKMOl4r?ot#6{Uq/l.b>3}7nrtwKKt+v&1^+2XZP#OyyeI48ri/mS:zopNS*AMI@Izpg.uQ7uMZxn37DQofVQ?T3v#8]lEmvG7w4$gmMYX]mpLiQCu7OiD-OQ&7%?(&{rSIFvwSED[p{B^@W!?uOo}GX:E.1Y}wCz[Bv1AuEWrIdMiJ{9Dw&)G&%RTav7i%bGc(lpErM9>])FZA85Kn4NeO)VJ?oWGtmHrzO&u0]D#mIOSo@=aeYoo+CmvX7yS-7TUnh3*e5+2%=eHwIK[+$V)&3{eAAB8hIii:.iIVtnYlA*E<4*lnIatLh#1@s^{r!oBMf#vTvv{n1DIi{.i>#?uavj=CAsQZ-UdkfXlH3ak?yBH5olpLmreX[Em!lplO6zc{Kr]8hLJVd4#>[A/LwDEzpiCe((u]%vW>F6GLkx*IzwFpM3Cp-1{eN0)vovADO4&h<[zaXJEY65q=BzKf0+K-J*P[#zp)>DHII%%A^r.=s4xS1oEBN).5[aB#w@Pti@yQda5L*OF<4EdxY39.*i:Z#hxN]H0C7>J])rqx-)$H6j5]B5RixS4DYMa3odS)V$vu=(]EkC3m2UM}6nQAu9vLfvkNFNSrEiGg-+lxxJ!Sq#UuFP/l57@WgHgcya96P(?xPc]p6zp@bA2tJuh?C[QP=V<)8%f?2S4qEUH>pvIhC{0W/[bP3yfa%#xGBVCf&0pAQTq5sF+UiQsM8{52KhK}oYG-3(ZZ-[Vd7?2BJ/*oEtVo>Hx/MUbwCOcF=#U=u*d?T}/i0hVg:Qy?Vuy.{vys5vc:Jf0uXo6!+P(+&JhDjiw?B+7{s$[9PHd69i#(p{yCQ4eD!^Ai!@6E/xKkGHj%%BOo06mAOT19:dAa@)T6nYEcMI3oZzr33s:A>z2!u!Fl!YQOPyHynVIA62#4M4a/@){JmU0{$llssF)2J49orW!mU20>A)IxR}6Ky+7Sq4w{+J5=]K4MK9@>nvI*.qNtqFt0mqF$Z4co9UX(bM}QHgkpmL+(Pp]Z?/E8-iJwc&G*]$/Ek.Nl.Ts4dGlhsfmk$2c##8UXX>ShEOtqzD]pLR!jcFyqH%@W9Z4oZ6P:DRd2bCZpqlIn<-z2bubmh]O9:#8QRu]7>H1IUX-3Y[&dm%S>(4ugyx=4&?-4oAlVR&907>v?]Q&:]N:Z:kOcjkR}4Z.O:jyc9Je]pyP?}jQtK2&-G!!-giFy+v6{vL8I>}Y:APLzUK$LFCgGOm9WPx#!B{qpU]suTVtB/#Q^oJ+gQjJoHj@aw&fOihvrR&0P?@&R==*N@PZLS:h94G&^WCC.rG7e)Li8]58E7soNu$v4NHVZpUvkSUJrWQIBQiWk$UG%N(:is+./<2xk}iJm2OUA)Nd)4P.{))]LA7w(^fzaeX<6.Rl3qce&PFVU6@Nf<&=imqCS[WiWuF=ny]d%%1tM07qGWSXrc&3ZsW=7@J(aICNkC[1W^[OC-p4DM*Z5y$5y5jv]DcWqV7WiC{Obqanl&og0vJT4QC.XC9E#mLvGy7HKB*>DsR@h/qG-S9:Lm:S=d+ci9h%a&CY>Zr4/I2AG9Ixiauwt%a74M9Y$Y@/r9@RQ5)Z4p5FHNS3[.xyU$Jm]yUs9}H(/fd!:!=:d%-#N++yG{8*CQxLhv}63C*LSf8D3?mfB{NvlZJ75EpM>rw!O$aRIx#x^}7y!p9%>E]HjM+EB3(K$le^&k-!(7K]Z+{gLm9>-ALsn/Y+*N5ijk(JVNI9Z}R&Sf+j82.DTG:}4OaCT5J9y?KJyi:3z(xpY06W0!5wL}d5IicAV19Ilc3R7Kv?%R5KPgcjHz9GLdD#?!&3f2{S:k(>An%V6F?-$-A#LAKU7]d:EFO$JK@.U*Irmc=sMmvK/UGJp-7s7*)KZP3D^[YH65eV}58rA3PzC^5.fuNc3DpFv!CXl8q2CXP3JNxO5oviTg/@d/RKIU84-G6z-YktA{Y.G7Kl?W!pPk)=Iwg%+s6@nXZHbSAY&@0e*eKsXOu(Fo:vXcE5<]*-QA!$[>r+uYsri?[^oou^5d)7XP6}?nA^}mr:HBpXt0Q*7u9rH]fsC6-0zMCEpzT>>%{gXCM4.}7}q]IoUdL=89fvxI&-pv[it8uhYc][qE58r3zYlMq4i.s:SzKU7L9RJ3lCGS!UOK([]&VWo}*0gRHk%KUA[Mq7RSLI+}[BZ*Zp5i6q$DK4HlN]wuR8jR5QF!9hA8ns8C2^gQRBf#}!Y!vBrON*+M%78l4mZF>HEMo>zF>oE?rFo-MQ0.G?HPzd(si1ThnM-er95RdPU1=WCV/@@Z&FE{q55[k1f$yv]PZ[=@T6QA=Zgw13Q.RmR(I0{=7R05JuvAi7LrrGuT8AGj1U5FcdtGxSL&SlX.oE42nB+^9h+uM]%SIL>KO@S4Wz+1O(qM3.^jZp&yBW}DY.Af9}Fl!MudSgP[.n/[6[a(y)YBG{X/v@))!VF({D!aq@Olbt]>8!.RmCfnDq<0yYP8HE]vsay3(FUs9[P53@.TuWZ(!.!2R>5yiYouT-1!rWdU7DM:Py/Ce#SBZA%7ByJYj*9y%Rg7UK&?.U%Wem1Hgg*HM&)S)pu&Gor=y&Di]AUajk7e)FeUHV8tqhFcMaLEtW#r}vWr=AI8)j}Xn#T9k}T{Yd%%mju-q1^P7k+!3Sa%cg!-guwrtl(U/RzkO@QbvFT+-@t>g%HuPxKW6-4RrnQvsUFFVQR=NT8]SLk&w$/-A/(4=A>%599r/FHKa2/B4/z<{Vhq!4qS}1=lLx@if9H[ax{lhlPGMRA^>HHA-jSjY=^)9orZ^O$A&7xxf{&9g.[7cqz&1YdOW9)Q-a+52)2&+pwPy$vrS>p0+ZMkk&HP=%sG+r3RLssAy)ck/yHexWNViRgzi}oJhMPCNU(Qb}3&TOTL$ICusl!efO}.pK%mxmnN>HKK(q:cO(TLm*xY(fBVaUsoLA&S(XNF}fxHTJ-Y(qXvZ72<=Z:TZrH@^J-:H*8x9*G/E#DboWIVRz2fr)P{mts0TIPo(MCyQz>)oG-{N/od2nE@-ULc}bdS)YzNg-0u)U={&B(s2cLqGrIjjvnWxE%[HZ:eC?s&$5VPli8f1oFNx[)8jNo%7SH5A+kqFf0Cg8G?tmt[O=)niWh$ajr9$72{6yPRukVYV/t%INx%hKzW>V[uyI5=3Kzhg(BQvNDWNiZpTTz3qb=IW0EI$PGwk7aNjHMT5x@UO9{idm]x>gkDuCN?TTl6-iuzYGS(ckLmiqd3qt4Dn/$.vg}-BF&L:pUDBd&oqDP}#)iT#fKJ=q{k*0VvuF>@SWZrBUhwc]Og+-DXfd%[G!hEIcXbtKKWo14VRE[RQOViKN3>zr1#*nerCkLovykRz2=krg2MkDu#af:6:1CzABmL)h2d&{Ulj<($g(::!ltSZ4Edu3&?>#UCXIn?8602.V}XPbNbnlO!-3-+}u?+=36L[@:*N}t/QnSabP9D*U>D@m15y+]Rw?kEGe(Mj2Ow.X.KmH@zJA%V-=sNpxErL-2ynjCxRMDTwl2i:QP1LZf{XH^7OoR=OUAzL*=5i5fqA36?$c5G<0m3C?llZs/%4&XlHUQG?4XExAVRWWcWjAdyjjRR@}}xcUh.N%3?fN2930-U=!w3hc#HNiks{de7x$=s0YeX5s4+xW@%+-+qXR[zxH>LU%EI&:n+@r18ugau>Ay}2IEwDi++%zeIdvC.BZmCEFM]H}.OHu*frs-Ovx?)SE0r$5S&<}COb/0w$r}$1+@[(Bpef1YtnKiIczr1iygxq8Q*DlLh)x0PDTVOx2@gw!yPrPqj1G}S}m@kMa@wM@>PGJb^a3DQVdP.2!NNk2iG@f-@hMWzUPm9x^&)B8-{{0GzN+}!qMhTCzWXc]K&bAalSK!o(cGJNK+-C4^sCp&25-D):eO6E2^Am[YTsW[NozgE64?NqckMK-%N&bG#V&H02vLWHEx&=j11/K:DG}cg*oH.8(XvUyypWe6ZFG)n^nv$NaFZh-i7DYoL7!Zkv1}haEpUj%n:MV6@o20}DZj6Bm/FyX26&09Y!f^aC:y7-AsfhAwSlZkn${uapL^k=f{1YBZ-IYL:wWxBYeui}HbI3RMve)AfkOy82(FCh&^LP43$+lD<@Y(VoGH:2KhW.pCOzHpiz{[yF#V.)vtqr=a6lVwjE(JAA@7a4n>txD[t{zcREQ35lhn^FZfzEl?Sfpi>qpkY]v[]dmy[CkJl:2w{WH}zo=TBxe.{ZX]Rl)7:W9)UZzh@xK/LtP=yVPU(u1@iHJM!wBxC$J)uJ@aXkmhGL:x?iKJ()%a8bpQ.hTebRF*(zmtL<2+&Erv%3IpWWg/RFEYz?rHG]?Wql[qdn=j{8)(d$*[C6P7nTv/REGZ*S>Nyn$K{m!xJp2gct!V@kS[vAR+1vYmPMDctp5zh%#H0xwqs9UXj3@*n]gGg(&j?u=d!a-jfCW#DR&EOBMu1yNq!hsYs}JY)FmqF=1%U-zmlYDAMyL)3VQ3=kWI387T*]j4[/T$G=:qe-k!F.oj[mZJ@xKWiGkz+:l@({+i}.11^Jx}ky1pmLj%a7#}>g{emy[hzrYkNv4#d0lVvm}eGnc79wQT14*Egm@Wu!voZl6Zr*%PK!Y]mi56m4FjCSBfz9C:c}#6qArIHT)T&((N}w+VB^BEx$.ium2Qo^nJQv?pY0WlU{G1MoAu&bflhW*&XtZnS7kb?-$46FH)XNJTn0!Io&H}g:7J1w5LtD5IjDRHb{4cK7O1rmhHObheqaRfdJLh3TA1D=7}cZ}C-Ti![IB1la.Vb#srRyehxz0=mZBAhLAB-UrEYl8qFZ[t8evlpweFua#2t6EWPvjb.Z<7s-AELi&egB(X[pXG$Q3M26R7b5?4z25%NT5=AbtuU6lV2oI{JHI?A/$83EwMwz-OX7azS[>7f{u{W+&nyru.:/.djauhISNCI9O&ZqeP}O-O6/?N&*C&IjDbpO6:%@]i4u/kGJWD&J6+R{w.:{vS}j@]rgB?*+U8}(!N:B:yeX2>0=@d@bkZ17J6T.b/A{{-Drm!D2V]y7saaYF@/YQojOuQB]bcTbUX9q>Bak(.wVp-<11Lu8u/<[uH:I]om-De2.m3zu&x[Q3N5Z?Y7nNMtKo:gT:PSK6?y:ZBx!0DdnlMmbWBlGUsO8uJn#SUXDdh-6(Vj{9V!Jz*+x/(R0N6Uf//KCx!7/av0JL4letPr}xVMi+i2*9LL(FEZVJ>ieMto0g&q#/S86ECl(pwrKzvlu-6*NxQiR6Vw^]fW6dSVsOJS8-W=b?*Qp9?Zaur/=$V3qZ89Ra4yu6jP->qJacc]E)cY=aQ(tVY}e@@p8dArJM.tKK&5h&7?B?R=yBJK}1%qGI:.m?s.KdL!clteNZXq7{{y1QR017dV&}^NDQ-iG75Dy&>3+qUKv.QRXusBR<))WXqS8JyF}GGCa57l=F^sLJ/OyzG=Umu%IKBDd=4[[vSyoMkFH9zfUk*a3erbU!)SM/V^aKp>!{30u5gAX#F8Xe%(}ug7*I?Cl)UY-9kgxm3:dPQ{s-!K:Cdt:n0&HO?c8rT%3))))B]0z-g$)0>xC!V@2wF!<)$]pKwd?=GHN)s2vpesy@ym8PwJ89.(l8BaT1![?x:+lwpBAv%rq3Ys^SFK&lmxHA=%Zx%YB=k6sj{Ltn*4es/?zZIp/<+nz/ih{[^H<:sw[2!uuPs>h4Bzm[fA/iTAzul{Nealo[.yAe/Pj6)$U+VKn{[L{MKnN$lxpoa74j)RXQmR&poHc4KZ6>T(o{HsbM30RP/