Quick Start

A small example of how to use the Lexbor library in program. Example for create Linux program using GCC compiler.

  1. Install lexbor library on your system.

  2. Create the following file named myhtml.c:

    #include <lexbor/html/parser.h>
    #include <lexbor/dom/interfaces/element.h>
    
    int
    main(int argc, const char *argv[])
    {
      lxb_status_t status;
      const lxb_char_t *tag_name;
      lxb_html_document_t *document;
    
      static const lxb_char_t html[] = "<div>Work fine!</div>";
      size_t html_len = sizeof(html) - 1;
    
      document = lxb_html_document_create();
      if (document == NULL) {
          exit(EXIT_FAILURE);
      }
    
      status = lxb_html_document_parse(document, html, html_len);
      if (status != LXB_STATUS_OK) {
          exit(EXIT_FAILURE);
      }
    
      tag_name = lxb_dom_element_qualified_name(lxb_dom_interface_element(document->body), 
                                                NULL);
    
      printf("Element tag name: %s\n", tag_name);
    
      lxb_html_document_destroy(document);
    
      return EXIT_SUCCESS;
    }
  3. Compile file myhtml.c using GCC: gcc myhtml.c -llexbor -o myhtml

  4. Done! You can run created programm: ./myhtml

Installation

Binary packages

Binaries are available for:

  • CentOS 6, 7
  • Debian 8, 9
  • Fedora 28, 29
  • RHEL 6, 7
  • Ubuntu 16.04, 18.04, 18.10, 19.04

CentOS

  1. To configure Lexbor repository, create the following file named /etc/yum.repos.d/lexbor.repo:

    [lexbor]
    name=lexbor repo
    baseurl=https://packages.lexbor.com/centos/$releasever/$basearch/
    gpgcheck=0
    enabled=1
  2. Install Lexbor base package and additional packages you would like to use.

    yum install liblexbor
    yum install liblexbor-devel

Debian

  1. Download Lexbor signing key used for our repositories and packages and add it to apt’s keyring:

    curl -O https://lexbor.com/keys/lexbor_signing.key
    apt-key add lexbor_signing.key
  2. To configure Lexbor repository, create the following file named /etc/apt/sources.list.d/lexbor.list:

    Debian 8:

    deb https://packages.lexbor.com/debian/ jessie liblexbor
    deb-src https://packages.lexbor.com/debian/ jessie liblexbor

    Debian 9:

    deb https://packages.lexbor.com/debian/ stretch liblexbor
    deb-src https://packages.lexbor.com/debian/ stretch liblexbor
  3. Install Lexbor base package and additional packages you would like to use.

    apt update
    apt install liblexbor
    apt install liblexbor-dev

Fedora

  1. To configure Lexbor repository, create the following file named /etc/yum.repos.d/lexbor.repo:

    [lexbor]
    name=lexbor repo
    baseurl=https://packages.lexbor.com/fedora/$releasever/$basearch/
    gpgcheck=0
    enabled=1
  2. Install Lexbor base package and additional packages you would like to use.

    yum install liblexbor
    yum install liblexbor-dev

RHEL

  1. To configure Lexbor repository, create the following file named /etc/yum.repos.d/lexbor.repo:

    [lexbor]
    name=lexbor repo
    baseurl=https://packages.lexbor.com/rhel/$releasever/$basearch/
    gpgcheck=0
    enabled=1
  2. Install Lexbor base package and additional packages you would like to use.

    yum install liblexbor
    yum install liblexbor-dev

Ubuntu

  1. Download Lexbor signing key used for our repositories and packages and add it to apt’s keyring:

    curl -O https://lexbor.com/keys/lexbor_signing.key
    apt-key add lexbor_signing.key
  2. To configure Lexbor repository, create the following file named /etc/apt/sources.list.d/lexbor.list:

    Ubuntu 16.04:

    deb https://packages.lexbor.com/ubuntu/ xenial liblexbor
    deb-src https://packages.lexbor.com/ubuntu/ xenial liblexbor

    Ubuntu 18.04:

    deb https://packages.lexbor.com/ubuntu/ bionic liblexbor
    deb-src https://packages.lexbor.com/ubuntu/ bionic liblexbor

    Ubuntu 18.10:

    deb https://packages.lexbor.com/ubuntu/ cosmic liblexbor
    deb-src https://packages.lexbor.com/ubuntu/ cosmic liblexbor

    Ubuntu 19.04:

    deb https://packages.lexbor.com/ubuntu/ disco liblexbor
    deb-src https://packages.lexbor.com/ubuntu/ disco liblexbor
  3. Install Lexbor base package and additional packages you would like to use.

    apt update
    apt install liblexbor
    apt install liblexbor-dev

Source code

For build and install Lexbor library from source code, use CMake (open-source, cross-platform build system).

Linux, *BSD, macOS

In root directory of project (/):

cmake .
make
sudo make install

Flags that can be passed to cmake:

FlagsDefaultDescription
LEXBOR_OPTIMIZATION_LEVEL-O2
LEXBOR_C_FLAGSDefault compilation flags to be used when compiling C files.
See port.cmake files in ports directory.
LEXBOR_WITHOUT_THREADSONNot used now, for the future
LEXBOR_BUILD_SHAREDONCreate shaded library
LEXBOR_BUILD_STATICONCreate static library
LEXBOR_INSTALL_HEADERSONThe header files will be installed if set to ON
LEXBOR_BUILD_TESTSOFFBuild tests
LEXBOR_BUILD_EXAMPLESOFFBuild examples

Windows

Use the CMake GUI.

For Windows with MSYS:

cmake . -G "Unix Makefiles"
make
make install

Debug and Sanitizer

cmake . -DCMAKE_C_FLAGS="-fsanitize=address -g" -DLEXBOR_OPTIMIZATION_LEVEL="-O0" -DLEXBOR_BUILD_TESTS=ON -DLEXBOR_BUILD_EXAMPLES=ON
make
make test

Examples

I recommend creating a separate directory to build the project. It can be easily removed together with all garbage.

All examples work from created build directory in the root directory of project:

mkdir build
cd build

Build together with tests:

cmake .. -DLEXBOR_BUILD_TESTS=ON
make
make test
sudo make install

Set installation location (prefix):

cmake .. -DCMAKE_INSTALL_PREFIX=/my/path/usr
make
make install

Installation only shared library (without headers):

cmake .. -DLEXBOR_BUILD_STATIC=OFF -DLEXBOR_INSTALL_HEADERS=OFF 
make
sudo make install

Build and run examples:

cmake .. -DLEXBOR_BUILD_EXAMPLES=ON
make
./examples/lexbor/html/element_create
./examples/lexbor/html/document_title

General

Dependencies

The project develops without external dependencies.

We are not fans of repeatedly writing all known algorithms, like AVL Tree, Binary Search Tree and so on, but it is important for us to use our own approach to creating objects and managing memory.
Most of the implemented algorithms are not "clean", the logic of them has somehow been changed/optimized for needs of the project.

There is no contradiction to using third-party code, but often all that is needed for a project is easier to write independently than to adapt someone's code, licenses.

Platform-dependent

Despite the fact that the project is written in pure C without external dependencies, the implementation of some functions depends for each platform. Namely: multithreading, timers, input-output, blocking (spinlock, mutex).

For this, a separate module port is implemented which has its own structure and build rules different from the other modules.

Memory

Four functions for working with dynamic memory:

void *
lexbor_malloc(size_t size);

void *
lexbor_calloc(size_t num, size_t size);

void *
lexbor_realloc(void *dst, size_t size);

void *
lexbor_free(void *dst);

Functions are defined in /source/lexbor/core/lexbor.h, implemented in/source/port/*/lexbor/core/memory.c and can be redefined.

From the names it is clear that these are bindings for standard functions malloc, calloc, realloc, free.
Unlike the standard free function, thelexbor_free function returns the value void * which is always equal to NULL - this is a kind of syntactic sugar, for not to nullify variables after free.

For example:

if (object->table != NULL) {
    object->table = lexbor_free(object->table);
}

Otherwise we would have to write:

if (object->table != NULL) {
    lexbor_free(object->table);
    object->table = NULL:
}

Statuses

If something inside a function can go "wrong", then the function should definitely say about it.

Two main rules when working with statuses:

  1. If the status is LXB_STATUS_OK (0), then everything is fine, otherwise something went wrong.
  2. We always return real statuses. That is, if memory is not allocated, then the LXB_STATUS_ERROR_MEMORY_ALLOCATION status will be returned, not some fake 0x1f1f.

There is a type of lxb_status_t (unsigned int), common to all, defined in /source/lexbor/core/types.h.
All available statuses can be seen in /source/lexbor/core/base.h file.

Naming

Almost all functions are created according to the following pattern:

lxb_<module-name>_<path-to-file>_<file-name>_<name>(...);
|_| |___________| |____________| |_________| |____|
 |        |              |            |        |
 |        -------        |            |        |
 ----------     |     ----     --------        |
          |     |     |        |               |
/source/lexbor/html/tree/open_elements.h       |
          |     |     |        |               |
          |    --     |        |       ---------
          |    |    ---      ---       |
          |    |    |        |         |
         |‾| |‾‾| |‾‾| |‾‾‾‾‾‾‾‾‾‾‾| |‾‾|
         lxb_html_tree_open_elements_find(...);

For example, let's take the function lxb_html_tree_create(...). Find it easily in /source/lexbor/html/tree.c

The exception is the main module (/source/lexbor/core) of the Lexbor project. Which has the following pattern:

lexbor_<path-to-file>_<file-name>_<name>(...);
|____| |____________| |_________| |____|
   |                      |         |
   -------           ------         |
         |           |              |
|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| |‾|             |
/source/lexbor/core/avl.h           |
|_________________| |_|             |
         |           |              |
         --------    |    -----------
                |    |    |
             |‾‾‾‾| |‾| |‾‾‾‾|
             lexbor_avl_create(...);

In other words, if in the project we have lexbor_* functions, then this means that it is the main module — core.

Headers

All paths relative to the /source directory. For example, if we need to include a header file from the html module which is in the directory /source/lexbor/html: #include "lexbor/html/tree.h".

Objects

Most structures/objects have the API to create, initialize, clean, and delete used the following pattern:

<structure-name> *
<function-prefix>_create(void);

lxb_status_t
<function-name>_init(<structure-name> *obj);

void
<function-name>_clean(<structure-name> *obj);

void
<function-name>_erase(<structure-name> *obj);

<structure-name> *
<function-name>_destroy(<structure-name> *obj, bool self_destroy);

The function of initialization object *_init can take any number of arguments and always returns lxb_status_t.
The cleanup functions *_clean and *_erase can return any value, usually void.

If you pass NULL as the first argument (object) to the initialization function *_init, the function will return LXB_STATUS_ERROR_OBJECT_NULL status.

If the *_destroy function calls with self_destroy equal true, then returned value always will be NULL, otherwise will be returned obj.
In *_destroy functions we always check object for NULL value. If object is NULL value, then function returned NULL value.

If the *_destroy function does not have the bool self_destroy argument, then object can only be created using the *_create function (not on stack).

Typical usage example:

lexbor_avl_t *avl = lexbor_avl_create();
lxb_status_t status = lexbor_avl_init(avl, 1024);

if (status != LXB_STATUS_OK) {
    lexbor_avl_node_destroy(avl, true);

    exit(EXIT_FAILURE);
}

/* Do something */

lexbor_avl_node_destroy(avl, true);

Example with object on stack:

lexbor_avl_t avl = {0};
lxb_status_t status = lexbor_avl_init(&avl, 1024);

if (status != LXB_STATUS_OK) {
    lexbor_avl_node_destroy(&avl, false);

    exit(EXIT_FAILURE);
}

/* Do something */

lexbor_avl_node_destroy(&avl, false);

It is worth noting that this approach is not an absolute postulate. There are cases when you have to implement a different API, but still, in most cases, it is.

Modules

The Lexbor project is modular. Theoretically, each module can be build separately from the whole project. Modules can have dependencies among themselves. For example, at the moment all modules are dependent on the core module.

All modules are located in the /source directory of the Lexbor project.

Versions

Each module contain information about its version in the header file base.h in the module root. For example, see /source/lexbor/html/base.h.

#define <MODULE-NAME>_VERSION_MAJOR 1
#define <MODULE-NAME>_VERSION_MINOR 0
#define <MODULE-NAME>_VERSION_PATCH 3

#define <MODULE-NAME>_VERSION_STRING LXB_STR(<MODULE-NAME>_VERSION_MAJOR) LXB_STR(.) \
                                     LXB_STR(<MODULE-NAME>_VERSION_MINOR) LXB_STR(.) \
                                     LXB_STR(<MODULE-NAME>_VERSION_PATCH)

Core

This is the base module. The Core contains all the necessary algorithms for the project: AVL Tree, Binary Search Tree, Array, Strings and other. All work with memory is implemented in this module.

This module is constantly evoluting, new algorithms are added and optimized existing ones.

Documentation for this module is not ready.

DOM

This module for working with DOM. For more detail, please, see DOM specifications.
DOM module contains functions for manipulating DOM tree: nodes, attributes, events.

Please, see documentation for DOM module.

HTML

This module contains implementation of HTML specification.

Implemented in this module: Tokenizer, Tree Builder, Parser, Fragment Parser, Interfaces for HTML Elements.

Please, see documentation for HTML module.

CSS

This module contains implementation of CSS specification.

Please, see documentation for CSS modules:

  1. Syntax
  2. Grammar
  3. Namespaces
  4. Selectors
  5. CSSOM
  6. Values
  7. Sizing
  8. Box
  9. Display
  10. Float
  11. Font
  12. Text
  13. Position
  14. Color
  15. Flexbox
  16. Background
  17. Content
  18. Overflow
  19. Media Queries
  20. Page
  21. Variables

Examples (source code)

All examples you can see in /examples directory on our repository.

How to compile and run the examples, please, see Build and Installation section.