├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── Release ├── makefile ├── objects.mk ├── sources.mk └── src │ └── subdir.mk ├── debian ├── changelog ├── compat ├── control ├── copyright ├── libcleri-dev.docs ├── libcleri-dev.examples ├── libcleri-dev.install ├── libcleri-dev.links ├── libcleri0.examples ├── libcleri0.install ├── libcleri0.links ├── libcleri0.symbols ├── rules ├── source │ └── format ├── tests │ └── control └── watch ├── examples ├── choice │ └── main.c ├── hi_iris │ └── main.c ├── json │ ├── json.c │ ├── json.h │ └── main.c ├── keyword │ └── main.c ├── list │ └── main.c ├── optional │ └── main.c ├── prio │ └── main.c ├── ref │ └── main.c ├── repeat │ └── main.c ├── sequence │ └── main.c ├── token │ └── main.c └── tokens │ └── main.c ├── inc └── cleri │ ├── choice.h │ ├── cleri.h │ ├── dup.h │ ├── expecting.h │ ├── grammar.h │ ├── keyword.h │ ├── kwcache.h │ ├── list.h │ ├── node.h │ ├── olist.h │ ├── optional.h │ ├── parse.h │ ├── prio.h │ ├── ref.h │ ├── regex.h │ ├── repeat.h │ ├── rule.h │ ├── sequence.h │ ├── this.h │ ├── token.h │ ├── tokens.h │ └── version.h ├── makefile.init ├── makefile.targets ├── src ├── choice.c ├── cleri.c ├── dup.c ├── expecting.c ├── grammar.c ├── keyword.c ├── kwcache.c ├── list.c ├── node.c ├── olist.c ├── optional.c ├── parse.c ├── prio.c ├── ref.c ├── regex.c ├── repeat.c ├── rule.c ├── sequence.c ├── this.c ├── token.c ├── tokens.c └── version.c └── test ├── helpers.h ├── test.h ├── test.sh ├── test_choice ├── sources └── test_choice.c ├── test_dup ├── sources └── test_dup.c ├── test_json_lang ├── sources └── test_json_lang.c ├── test_keyword ├── sources └── test_keyword.c ├── test_list ├── sources └── test_list.c ├── test_optional ├── sources └── test_optional.c ├── test_prio ├── sources └── test_prio.c ├── test_ref ├── sources └── test_ref.c ├── test_regex ├── sources └── test_regex.c ├── test_repeat ├── sources └── test_repeat.c ├── test_sequence ├── sources └── test_sequence.c ├── test_thingsdb_lang ├── sources └── test_thingsdb_lang.c ├── test_token ├── sources └── test_token.c ├── test_tokens ├── sources └── test_tokens.c └── test_version ├── sources └── test_version.c /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Install dependencies 16 | run: | 17 | sudo apt-get install -y libpcre2-dev valgrind 18 | - name: Run tests 19 | run: | 20 | cd ./Release/ 21 | make test 22 | - name: Compile code 23 | run: | 24 | cd ./Release/ 25 | CFLAGS="-Werror -Winline -std=gnu89" make 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | *.d 23 | 24 | # Executables 25 | *.exe 26 | *.out 27 | *.app 28 | *.i*86 29 | *.x86_64 30 | *.hex 31 | 32 | # Debug files 33 | *.dSYM/ 34 | 35 | # Visual Studio Code 36 | .vscode/ 37 | 38 | # Folder for build artifacts 39 | [Bb]uild/ 40 | 41 | # Debian 42 | debian/.debhelper/ 43 | debian/debhelper-build-stamp 44 | debian/files 45 | debian/libcleri-dev.debhelper.log 46 | debian/libcleri-dev.substvars 47 | debian/libcleri-dev/ 48 | debian/libcleri0.debhelper.log 49 | debian/libcleri0.substvars 50 | debian/libcleri0/ 51 | debian/tmp/ 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Install dependencies 2 | ``` 3 | sudo apt-get install libpcre2-8-0 4 | ``` 5 | 6 | # Running tests 7 | ``` 8 | make --directory ./Release test 9 | ``` 10 | 11 | # Build 12 | ``` 13 | make --directory ./Release clean 14 | make --directory ./Release 15 | ``` 16 | 17 | # Install 18 | ``` 19 | # You might want to uninstall first... 20 | sudo make --directory ./Release uninstall 21 | sudo make --directory ./Release install 22 | ``` 23 | 24 | # Building deb package 25 | Make sure the required tools are installed 26 | ``` 27 | sudo apt-get install devscripts lintian 28 | ``` 29 | 30 | When symbols are changed: 31 | ``` 32 | dpkg-gensymbols -eRelease/libcleri.so -plibcleri0 33 | ``` 34 | 35 | In case of a new package, update the changelog. 36 | Skip this step if you just want to rebuild the current deb version. 37 | ``` 38 | debchange 39 | ``` 40 | 41 | In case of any changes, commit 42 | ``` 43 | git commit -am 'some work that has been done' 44 | ``` 45 | 46 | Create archive from code 47 | ``` 48 | git archive -o ../libcleri_1.0.0.orig.tar.gz master 49 | ``` 50 | 51 | Build deb package 52 | ``` 53 | debuild -us -uc 54 | ``` 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Cesbit 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /Release/makefile: -------------------------------------------------------------------------------- 1 | -include ../makefile.init 2 | 3 | RM := rm -rf 4 | CC ?= gcc 5 | 6 | -include sources.mk 7 | -include src/subdir.mk 8 | -include subdir.mk 9 | -include objects.mk 10 | 11 | ifneq ($(MAKECMDGOALS),clean) 12 | ifneq ($(strip $(C_DEPS)),) 13 | -include $(C_DEPS) 14 | endif 15 | endif 16 | 17 | -include ../makefile.defs 18 | 19 | OS := $(shell uname) 20 | ifeq ($(OS),Darwin) 21 | FN := libcleri.dylib 22 | INSTALL_PATH := /usr/local 23 | SO_NAME := install_name 24 | else 25 | FN := libcleri.so 26 | INSTALL_PATH := /usr 27 | SO_NAME := soname 28 | endif 29 | 30 | # All Target 31 | all: libcleri 32 | 33 | # Tool invocations 34 | libcleri: $(OBJS) $(USER_OBJS) 35 | @echo 'Building target: $@' 36 | @echo "Invoking: Cross $(CC) Linker" 37 | $(CC) -shared -Wl,-$(SO_NAME),$(FN).$(MAJOR) -o $(FN) $(OBJS) $(USER_OBJS) $(LIBS) $(LDFLAGS) 38 | @chmod -x $(FN) 39 | @echo 'Finished building target: $@' 40 | @echo ' ' 41 | 42 | # Other Targets 43 | clean: 44 | -$(RM) $(LIBRARIES)$(OBJS)$(C_DEPS) $(FN) 45 | -@echo ' ' 46 | 47 | .PHONY: all clean dependents 48 | .SECONDARY: 49 | 50 | -include ../makefile.targets 51 | 52 | test: 53 | @cd ../test && ./test.sh 54 | -------------------------------------------------------------------------------- /Release/objects.mk: -------------------------------------------------------------------------------- 1 | USER_OBJS := 2 | 3 | LIBS := -lpcre2-8 4 | -------------------------------------------------------------------------------- /Release/sources.mk: -------------------------------------------------------------------------------- 1 | OBJ_SRCS := 2 | ASM_SRCS := 3 | C_SRCS := 4 | O_SRCS := 5 | S_UPPER_SRCS := 6 | EXECUTABLES := 7 | OBJS := 8 | C_DEPS := 9 | 10 | SUBDIRS := \ 11 | src \ 12 | 13 | -------------------------------------------------------------------------------- /Release/src/subdir.mk: -------------------------------------------------------------------------------- 1 | C_SRCS += \ 2 | ../src/choice.c \ 3 | ../src/dup.c \ 4 | ../src/expecting.c \ 5 | ../src/grammar.c \ 6 | ../src/keyword.c \ 7 | ../src/kwcache.c \ 8 | ../src/list.c \ 9 | ../src/node.c \ 10 | ../src/cleri.c \ 11 | ../src/olist.c \ 12 | ../src/optional.c \ 13 | ../src/parse.c \ 14 | ../src/prio.c \ 15 | ../src/ref.c \ 16 | ../src/regex.c \ 17 | ../src/repeat.c \ 18 | ../src/rule.c \ 19 | ../src/sequence.c \ 20 | ../src/this.c \ 21 | ../src/token.c \ 22 | ../src/tokens.c \ 23 | ../src/version.c 24 | 25 | 26 | OBJS += \ 27 | ./src/choice.o \ 28 | ./src/dup.o \ 29 | ./src/expecting.o \ 30 | ./src/grammar.o \ 31 | ./src/keyword.o \ 32 | ./src/kwcache.o \ 33 | ./src/list.o \ 34 | ./src/node.o \ 35 | ./src/cleri.o \ 36 | ./src/olist.o \ 37 | ./src/optional.o \ 38 | ./src/parse.o \ 39 | ./src/prio.o \ 40 | ./src/ref.o \ 41 | ./src/regex.o \ 42 | ./src/repeat.o \ 43 | ./src/rule.o \ 44 | ./src/sequence.o \ 45 | ./src/this.o \ 46 | ./src/token.o \ 47 | ./src/tokens.o \ 48 | ./src/version.o 49 | 50 | C_DEPS += \ 51 | ./src/choice.d \ 52 | ./src/dup.d \ 53 | ./src/expecting.d \ 54 | ./src/grammar.d \ 55 | ./src/keyword.d \ 56 | ./src/kwcache.d \ 57 | ./src/list.d \ 58 | ./src/node.d \ 59 | ./src/cleri.d \ 60 | ./src/olist.d \ 61 | ./src/optional.d \ 62 | ./src/parse.d \ 63 | ./src/prio.d \ 64 | ./src/ref.d \ 65 | ./src/regex.d \ 66 | ./src/repeat.d \ 67 | ./src/rule.d \ 68 | ./src/sequence.d \ 69 | ./src/this.d \ 70 | ./src/token.d \ 71 | ./src/tokens.d \ 72 | ./src/version.d 73 | 74 | src/%.o: ../src/%.c 75 | @echo 'Building file: $<' 76 | @echo "Invoking: Cross $(CC) Compiler" 77 | $(CC) -DNDEBUG -I../inc -O3 -Winline -Wall $(CPPFLAGS) $(CFLAGS) -c -fmessage-length=0 -fPIC -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@)" -o "$@" "$<" 78 | @echo 'Finished building: $<' 79 | @echo ' ' 80 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | libcleri (1.0.2-0~tt1) unstable; urgency=medium 2 | 3 | * Fixed makefile to correct version. 4 | * Do not hard-code GCC as compiler #24, @giordano 5 | 6 | -- Jeroen van der Heijden Thu, 26 Oct 2023 15:03:14 +0200 7 | 8 | libcleri (1.0.1-0~tt1.1) UNRELEASED; urgency=medium 9 | 10 | * Non-maintainer upload. 11 | * Fix FTCBFS: (Closes: #-1) 12 | + Let dh_auto_build pass cross tools to make. 13 | + cross.patch: Make gcc substitutable. 14 | 15 | -- Helmut Grohne Sun, 17 May 2020 12:14:38 +0200 16 | 17 | libcleri (1.0.1-0~tt1) unstable; urgency=medium 18 | 19 | * Changed company name to Cesbit. 20 | * Removed regular expression assertion. 21 | 22 | -- Jeroen van der Heijden Mon, 17 Oct 2022 09:24 +0100 23 | 24 | libcleri (1.0.0-0~tt1) unstable; urgency=medium 25 | 26 | * Remove cleri_children_t to reduce mallocs, issue #18 27 | * Fixed bug with `CLERI_FLAG_EXPECTING_DISABLED` flag 28 | * Ordered expecting chages for minor speed improvement 29 | 30 | -- Jeroen van der Heijden Mon, 03 Jan 2022 16:20:49 +0100 31 | 32 | libcleri (0.12.2-0~tt1) unstable; urgency=medium 33 | 34 | * Fixed bug in Prio element 35 | 36 | -- Jeroen van der Heijden Tue, 10 Aug 2021 11:08:22 +0200 37 | 38 | libcleri (0.11.1-0~tt1) unstable; urgency=medium 39 | 40 | * Changed start of line numbering from 0 to 1 41 | 42 | -- Jeroen van der Heijden Tue, 12 Nov 2019 08:44:10 +0100 43 | 44 | libcleri (0.11.0-0~tt1) unstable; urgency=medium 45 | 46 | * Added cleri_parse2() which accepts an additional `flags` argument 47 | * Improved syntax error handling 48 | * Set correct position when nested error in syntax is found 49 | 50 | -- Jeroen van der Heijden Tue, 22 Oct 2019 10:59:14 +0200 51 | 52 | libcleri (0.10.1-0~alpha1-tt1) unstable; urgency=medium 53 | 54 | * Added option to disable generating expecting info 55 | 56 | -- Jeroen van der Heijden Mon, 12 Nov 2018 14:34:16 +0100 57 | 58 | libcleri (0.10.1-0~alpha0-tt1) unstable; urgency=medium 59 | 60 | * Added `void *data` to node type for public usage 61 | * Children will only be added to a node when necessary 62 | (Be carefull that accessing the first child therefore 63 | must be checked for NULL whereas in previous versions, 64 | one could immediately check the `children->node` pointer) 65 | 66 | -- Jeroen van der Heijden Wed, 31 Oct 2018 22:22:05 +0100 67 | 68 | libcleri (0.10.0-0~tt1) unstable; urgency=medium 69 | 70 | * Update deb test 71 | * Update Vsc fields 72 | * New upstream release 73 | - Fixed expecting when parsing a list (Issue: #11) 74 | - Fixed `is_valid` property when parsing empty strings 75 | (Issue #12) 76 | - Added `cleri_parse_strn()` function for generating a textual 77 | parse result 78 | 79 | -- Jeroen van der Heijden Fri, 12 Oct 2018 15:28:28 +0200 80 | 81 | libcleri (0.9.4-2) unstable; urgency=medium 82 | 83 | * Update and add Vcs-* fields for move to salsa (Closes: #890701) 84 | * Add autopkgtest by compiling the examples 85 | * Install examples 86 | 87 | -- Paul Gevers Fri, 27 Jul 2018 21:04:02 +0200 88 | 89 | libcleri (0.9.4-1) unstable; urgency=medium 90 | 91 | * New upstream release 92 | - Migrated to pcre2 after being informed by us about the state of 93 | pcre3 94 | * Drop obsolete patch 95 | * Make packages as Multi-Arch:same 96 | * Bump standards to 4.1.2 (no changes) 97 | 98 | -- Paul Gevers Sun, 03 Dec 2017 19:24:17 +0100 99 | 100 | libcleri (0.9.3-1) unstable; urgency=medium 101 | 102 | * Initial release. (Closes: #882677) 103 | 104 | -- Paul Gevers Sun, 26 Nov 2017 08:11:22 +0100 105 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: libcleri 2 | Section: libs 3 | Priority: optional 4 | Maintainer: SiriDB Maintainers 5 | Uploaders: 6 | Jeroen van der Heijden , 7 | Paul Gevers , 8 | Rules-Requires-Root: no 9 | Build-Depends: 10 | debhelper (>= 10~), 11 | dh-exec, 12 | libpcre2-dev, 13 | Homepage: https://siridb.net/ 14 | Vcs-Browser: https://salsa.debian.org/siridb-team/libcleri 15 | Vcs-Git: https://salsa.debian.org/siridb-team/libcleri.git 16 | Standards-Version: 4.1.2 17 | 18 | Package: libcleri0 19 | Architecture: any 20 | Multi-Arch: same 21 | Depends: 22 | ${misc:Depends}, 23 | ${shlibs:Depends}, 24 | Description: language parser library 25 | Libcleri is a powerful tool to build languages. From a built language, 26 | libcleri can automatically create parse trees, which are data structures 27 | representing how a grammar matches input. It also provides feedback in case 28 | the input does not match the language. This can be useful for auto-completion, 29 | suggestions or error handling. 30 | 31 | Package: libcleri-dev 32 | Section: libdevel 33 | Architecture: any 34 | Multi-Arch: same 35 | Depends: 36 | libcleri0 (= ${binary:Version}), 37 | ${misc:Depends}, 38 | Description: language parser library - development files 39 | Libcleri is a powerful tool to build languages. From a built language, 40 | libcleri can automatically create parse trees, which are data structures 41 | representing how a grammar matches input. It also provides feedback in case 42 | the input does not match the language. This can be useful for auto-completion, 43 | suggestions or error handling. 44 | . 45 | This package contains the development files. 46 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | 3 | Files: * 4 | Copyright: 2021, Cesbit 5 | License: Expat 6 | 7 | Files: debian/* 8 | Copyright: 2017 Paul Gevers 9 | License: Expat 10 | 11 | License: Expat 12 | The MIT License 13 | . 14 | Permission is hereby granted, free of charge, to any person 15 | obtaining a copy of this software and associated 16 | documentation files (the "Software"), to deal in the Software 17 | without restriction, including without limitation the rights to 18 | use, copy, modify, merge, publish, distribute, sublicense, 19 | and/or sell copies of the Software, and to permit persons to 20 | whom the Software is furnished to do so, subject to the 21 | following conditions: 22 | . 23 | The above copyright notice and this permission notice shall 24 | be included in all copies or substantial portions of the 25 | Software. 26 | . 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT 28 | WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 29 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 30 | MERCHANTABILITY, FITNESS FOR A PARTICULAR 31 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT 32 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 33 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 34 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 35 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 36 | CONNECTION WITH THE SOFTWARE OR THE USE OR 37 | OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /debian/libcleri-dev.docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/libcleri-dev.examples: -------------------------------------------------------------------------------- 1 | examples/* 2 | -------------------------------------------------------------------------------- /debian/libcleri-dev.install: -------------------------------------------------------------------------------- 1 | inc/cleri/*.h usr/include/cleri 2 | -------------------------------------------------------------------------------- /debian/libcleri-dev.links: -------------------------------------------------------------------------------- 1 | #! /usr/bin/dh-exec 2 | usr/lib/${DEB_HOST_MULTIARCH}/libcleri.so.${SONAME} usr/lib/${DEB_HOST_MULTIARCH}/libcleri.so 3 | -------------------------------------------------------------------------------- /debian/libcleri0.examples: -------------------------------------------------------------------------------- 1 | examples/* 2 | -------------------------------------------------------------------------------- /debian/libcleri0.install: -------------------------------------------------------------------------------- 1 | #! /usr/bin/dh-exec 2 | Release/libcleri.so => /usr/lib/${DEB_HOST_MULTIARCH}/libcleri.so.${DEB_VERSION_UPSTREAM} 3 | -------------------------------------------------------------------------------- /debian/libcleri0.links: -------------------------------------------------------------------------------- 1 | #! /usr/bin/dh-exec 2 | usr/lib/${DEB_HOST_MULTIARCH}/libcleri.so.${DEB_VERSION_UPSTREAM} usr/lib/${DEB_HOST_MULTIARCH}/libcleri.so.${SONAME} 3 | -------------------------------------------------------------------------------- /debian/libcleri0.symbols: -------------------------------------------------------------------------------- 1 | libcleri.so.0 libcleri0 #MINVER# 2 | CLERI_EMPTY_NODE@Base 0.9.3 3 | CLERI_END_OF_STATEMENT@Base 0.9.3 4 | CLERI_THIS@Base 0.9.3 5 | cleri__expecting_combine@Base 0.9.3 6 | cleri__expecting_free@Base 0.9.3 7 | cleri__expecting_new@Base 0.9.3 8 | cleri__expecting_set_mode@Base 0.9.3 9 | cleri__expecting_update@Base 0.9.3 10 | cleri__kwcache_free@Base 0.9.3 11 | cleri__kwcache_match@Base 0.9.3 12 | cleri__kwcache_new@Base 0.9.3 13 | cleri__node_dup@Base 1.0.0~ 14 | cleri__node_free@Base 0.9.3 15 | cleri__node_new@Base 0.9.3 16 | cleri__olist_append@Base 0.9.3 17 | cleri__olist_append_nref@Base 0.9.3 18 | cleri__olist_cancel@Base 0.9.3 19 | cleri__olist_empty@Base 0.9.3 20 | cleri__olist_free@Base 0.9.3 21 | cleri__olist_new@Base 0.9.3 22 | cleri__olist_unique@Base 0.11.0~ 23 | cleri__parse_walk@Base 0.9.3 24 | cleri__rule@Base 0.9.3 25 | cleri__rule_init@Base 0.9.3 26 | cleri_choice@Base 0.9.3 27 | cleri_decref@Base 0.9.3 28 | cleri_dup@Base 0.9.3 29 | cleri_free@Base 0.9.3 30 | cleri_grammar@Base 0.9.3 31 | cleri_grammar_free@Base 0.9.3 32 | cleri_incref@Base 0.9.3 33 | cleri_keyword@Base 0.9.3 34 | cleri_list@Base 0.9.3 35 | cleri_new@Base 0.9.3 36 | cleri_optional@Base 0.9.3 37 | cleri_parse2@Base 0.10.1~ 38 | cleri_parse_expect_start@Base 0.9.3 39 | cleri_parse_free@Base 0.9.3 40 | cleri_parse_strn@Base 0.10.0~ 41 | cleri_prio@Base 0.9.3 42 | cleri_ref@Base 0.9.3 43 | cleri_ref_set@Base 0.9.3 44 | cleri_regex@Base 0.9.3 45 | cleri_repeat@Base 0.9.3 46 | cleri_sequence@Base 0.9.3 47 | cleri_token@Base 0.9.3 48 | cleri_tokens@Base 0.9.3 49 | cleri_version@Base 0.10.1~ 50 | 51 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DEB_BUILD_MAINT_OPTIONS=hardening=+all 4 | 5 | include /usr/share/dpkg/pkg-info.mk 6 | # Use both these variable to create the right libcleri.so* files/links 7 | export DEB_VERSION_UPSTREAM 8 | # Should be dynamic or unnecessary in the future, for now, just hard-code 9 | export SONAME=0 10 | 11 | %: 12 | dh $@ 13 | 14 | override_dh_auto_build-arch: 15 | dh_auto_build --sourcedirectory=Release -- all 16 | 17 | override_dh_auto_clean: 18 | $(MAKE) --directory=Release clean 19 | dh_auto_clean 20 | 21 | override_dh_fixperms: 22 | dh_fixperms 23 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/tests/control: -------------------------------------------------------------------------------- 1 | Test-Command: USELIB=1 make --directory=Release test 2 | Features: test-name=libcleri-unit-tests 3 | Depends: @, @builddeps@ 4 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=4 2 | opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%libcleri-$1.tar.gz%" \ 3 | https://github.com/cesbit/libcleri/releases \ 4 | (?:.*?/)?v?(\d[\d.]*)\.tar\.gz 5 | -------------------------------------------------------------------------------- /examples/choice/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestChoice = "goodbye"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * k_hello = cleri_keyword(0, "hello", 0); 10 | cleri_t * k_goodbye = cleri_keyword(0, "goodbye", 0); 11 | cleri_t * choice = cleri_choice( 12 | 0, // gid, not used in this example 13 | 0, // stop at first match 14 | 2, // number of elements 15 | k_hello, k_goodbye); // elements 16 | 17 | /* create grammar */ 18 | cleri_grammar_t * grammar = cleri_grammar(choice, NULL); 19 | 20 | /* parse some test string */ 21 | cleri_parse_t * pr = cleri_parse(grammar, TestChoice); 22 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestChoice); 23 | 24 | /* cleanup */ 25 | cleri_parse_free(pr); 26 | cleri_grammar_free(grammar); 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /examples/hi_iris/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void test_str(cleri_grammar_t * grammar, const char * str) 5 | { 6 | cleri_parse_t * pr = cleri_parse(grammar, str); 7 | printf("Test string '%s': %s\n", str, pr->is_valid ? "true" : "false"); 8 | cleri_parse_free(pr); 9 | } 10 | 11 | int main(void) 12 | { 13 | /* define grammar */ 14 | cleri_t * k_hi = cleri_keyword(0, "hi", 0); 15 | cleri_t * r_name = cleri_regex(0, "^(?:\"(?:[^\"]*)\")+"); 16 | cleri_t * start = cleri_sequence(0, 2, k_hi, r_name); 17 | 18 | /* compile grammar */ 19 | cleri_grammar_t * my_grammar = cleri_grammar(start, NULL); 20 | 21 | /* test some strings */ 22 | test_str(my_grammar, "hi \"Iris\""); // true 23 | test_str(my_grammar, "bye \"Iris\""); // false 24 | 25 | /* cleanup grammar */ 26 | cleri_grammar_free(my_grammar); 27 | 28 | return 0; 29 | } -------------------------------------------------------------------------------- /examples/json/json.c: -------------------------------------------------------------------------------- 1 | /* 2 | * json.c 3 | * 4 | * This grammar is generated using the Grammar.export_c() method and 5 | * should be used with the cleri module. 6 | * 7 | * Source class: JsonGrammar 8 | * Created at: 2017-06-20 22:05:27 9 | */ 10 | 11 | #include "json.h" 12 | #include 13 | 14 | #define CLERI_CASE_SENSITIVE 0 15 | #define CLERI_CASE_INSENSITIVE 1 16 | 17 | #define CLERI_FIRST_MATCH 0 18 | #define CLERI_MOST_GREEDY 1 19 | 20 | cleri_grammar_t * compile_grammar(void) 21 | { 22 | cleri_t * START = cleri_ref(); 23 | cleri_t * r_string = cleri_regex(CLERI_GID_R_STRING, "^(\")(?:(?=(\\\\?))\\2.)*?\\1"); 24 | cleri_t * r_float = cleri_regex(CLERI_GID_R_FLOAT, "^-?[0-9]+\\.?[0-9]+"); 25 | cleri_t * r_integer = cleri_regex(CLERI_GID_R_INTEGER, "^-?[0-9]+"); 26 | cleri_t * k_true = cleri_keyword(CLERI_GID_K_TRUE, "true", CLERI_CASE_SENSITIVE); 27 | cleri_t * k_false = cleri_keyword(CLERI_GID_K_FALSE, "false", CLERI_CASE_SENSITIVE); 28 | cleri_t * k_null = cleri_keyword(CLERI_GID_K_NULL, "null", CLERI_CASE_SENSITIVE); 29 | cleri_t * json_map_item = cleri_sequence( 30 | CLERI_GID_JSON_MAP_ITEM, 31 | 3, 32 | r_string, 33 | cleri_token(CLERI_NONE, ":"), 34 | START 35 | ); 36 | cleri_t * json_map = cleri_sequence( 37 | CLERI_GID_JSON_MAP, 38 | 3, 39 | cleri_token(CLERI_NONE, "{"), 40 | cleri_list(CLERI_NONE, json_map_item, cleri_token(CLERI_NONE, ","), 0, 0, 0), 41 | cleri_token(CLERI_NONE, "}") 42 | ); 43 | cleri_t * json_array = cleri_sequence( 44 | CLERI_GID_JSON_ARRAY, 45 | 3, 46 | cleri_token(CLERI_NONE, "["), 47 | cleri_list(CLERI_NONE, START, cleri_token(CLERI_NONE, ","), 0, 0, 0), 48 | cleri_token(CLERI_NONE, "]") 49 | ); 50 | cleri_ref_set(START, cleri_choice( 51 | CLERI_GID_START, 52 | CLERI_MOST_GREEDY, 53 | 8, 54 | r_string, 55 | r_float, 56 | r_integer, 57 | k_true, 58 | k_false, 59 | k_null, 60 | json_map, 61 | json_array 62 | )); 63 | 64 | cleri_grammar_t * grammar = cleri_grammar(START, "^\\w+"); 65 | 66 | return grammar; 67 | } 68 | -------------------------------------------------------------------------------- /examples/json/json.h: -------------------------------------------------------------------------------- 1 | /* 2 | * json.h 3 | * 4 | * This grammar is generated using the Grammar.export_c() method and 5 | * should be used with the cleri module. 6 | * 7 | * Source class: JsonGrammar 8 | * Created at: 2017-06-20 21:53:53 9 | */ 10 | #ifndef CLERI_EXPORT_JSON_H_ 11 | #define CLERI_EXPORT_JSON_H_ 12 | 13 | #include 14 | 15 | cleri_grammar_t * compile_grammar(void); 16 | 17 | enum cleri_grammar_ids { 18 | CLERI_NONE, // used for objects with no name 19 | CLERI_GID_JSON_ARRAY, 20 | CLERI_GID_JSON_MAP, 21 | CLERI_GID_JSON_MAP_ITEM, 22 | CLERI_GID_K_FALSE, 23 | CLERI_GID_K_NULL, 24 | CLERI_GID_K_TRUE, 25 | CLERI_GID_R_FLOAT, 26 | CLERI_GID_R_INTEGER, 27 | CLERI_GID_R_STRING, 28 | CLERI_GID_START, 29 | CLERI_END // can be used to get the enum length 30 | }; 31 | 32 | #endif /* CLERI_EXPORT_JSON_H_ */ 33 | -------------------------------------------------------------------------------- /examples/json/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "json.h" 4 | 5 | const char * TestJSON = "{\"Name\": \"Iris\", \"Age\": 4}"; 6 | 7 | int main(void) 8 | { 9 | cleri_grammar_t * json_grammar = compile_grammar(); 10 | cleri_parse_t * pr = cleri_parse(json_grammar, TestJSON); 11 | 12 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestJSON); 13 | 14 | /* cleanup */ 15 | cleri_parse_free(pr); 16 | cleri_grammar_free(json_grammar); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /examples/keyword/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestKeyword = "Tic-Tac-Toe"; 5 | 6 | int main(void) 7 | { 8 | /* define keyword */ 9 | cleri_t * k_tictactoe = cleri_keyword( 10 | 0, // gid, not used in this example 11 | "tic-tac-toe", // keyword 12 | 1); // case insensitive 13 | 14 | /* create grammar with custom keyword regular expression match */ 15 | cleri_grammar_t * grammar = cleri_grammar(k_tictactoe, "^[A-Za-z-]+"); 16 | 17 | /* parse some test string */ 18 | cleri_parse_t * pr = cleri_parse(grammar, TestKeyword); 19 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestKeyword); 20 | 21 | /* cleanup */ 22 | cleri_parse_free(pr); 23 | cleri_grammar_free(grammar); 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /examples/list/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestList = "ni, ni, ni, ni, ni"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * list = cleri_list( 10 | 0, // gid, not used in this example 11 | cleri_keyword(0, "ni", 0), // repeated element 12 | cleri_token(0, ","), // delimiter element 13 | 0, // min n times 14 | 0, // max n times (0 for unlimited) 15 | 0); // disallow ending with a delimiter 16 | 17 | /* create grammar */ 18 | cleri_grammar_t * grammar = cleri_grammar(list, NULL); 19 | 20 | /* parse some test string */ 21 | cleri_parse_t * pr = cleri_parse(grammar, TestList); 22 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestList); 23 | 24 | /* cleanup */ 25 | cleri_parse_free(pr); 26 | cleri_grammar_free(grammar); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /examples/optional/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestOptional = "hello"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * k_hello = cleri_keyword(0, "hello", 0); 10 | cleri_t * k_there = cleri_keyword(0, "there", 0); 11 | cleri_t * optional = cleri_optional( 12 | 0, // gid, not used in this example 13 | k_there); // optional element 14 | cleri_t * greet = cleri_sequence( 15 | 0, // gid, not used in this example 16 | 2, // number of elements 17 | k_hello, optional); // elements 18 | 19 | /* create grammar */ 20 | cleri_grammar_t * grammar = cleri_grammar(greet, NULL); 21 | 22 | /* parse some test string */ 23 | cleri_parse_t * pr = cleri_parse(grammar, TestOptional); 24 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestOptional); 25 | 26 | /* cleanup */ 27 | cleri_parse_free(pr); 28 | cleri_grammar_free(grammar); 29 | 30 | return 0; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /examples/prio/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestPrio = "(ni or ni) and (ni or ni)"; 5 | 6 | int main(void) 7 | { 8 | /* 9 | * define grammar. 10 | * 11 | * Note: The third and fourth element are using a reference to the prio 12 | * element at the same position in the string as the prio element. 13 | * This is why a forward reference cannot be used for this example. 14 | */ 15 | cleri_t * prio = cleri_prio( 16 | 0, // gid, not used in the example 17 | 4, // number of elements 18 | cleri_keyword(0, "ni", 0), // first element 19 | cleri_sequence(0, 3, // second element 20 | cleri_token(0, "("), 21 | CLERI_THIS, 22 | cleri_token(0, ")")), 23 | cleri_sequence(0, 3, // third element 24 | CLERI_THIS, 25 | cleri_keyword(0, "or", 0), 26 | CLERI_THIS), 27 | cleri_sequence(0, 3, // fourth element 28 | CLERI_THIS, 29 | cleri_keyword(0, "and", 0), 30 | CLERI_THIS)); 31 | 32 | /* create grammar */ 33 | cleri_grammar_t * grammar = cleri_grammar(prio, NULL); 34 | 35 | /* parse some test string */ 36 | cleri_parse_t * pr = cleri_parse(grammar, TestPrio); 37 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestPrio); 38 | 39 | /* cleanup */ 40 | cleri_parse_free(pr); 41 | cleri_grammar_free(grammar); 42 | 43 | return 0; 44 | } 45 | 46 | -------------------------------------------------------------------------------- /examples/ref/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestRef = "[ni, ni, [ni, [], [ni, ni]]]"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * ref = cleri_ref(); 10 | cleri_t * choice = cleri_choice( 11 | 0, 0, 2, cleri_keyword(0, "ni", 0), ref); 12 | 13 | cleri_ref_set(ref, cleri_sequence( 14 | 0, 15 | 3, 16 | cleri_token(0, "["), 17 | cleri_list(0, choice, cleri_token(0, ","), 0, 0, 0), 18 | cleri_token(0, "]"))); 19 | 20 | /* create grammar */ 21 | cleri_grammar_t * grammar = cleri_grammar(ref, NULL); 22 | 23 | /* parse some test string */ 24 | cleri_parse_t * pr = cleri_parse(grammar, TestRef); 25 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestRef); 26 | 27 | /* cleanup */ 28 | cleri_parse_free(pr); 29 | cleri_grammar_free(grammar); 30 | 31 | return 0; 32 | } -------------------------------------------------------------------------------- /examples/repeat/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestRepeat = "ni ni ni ni ni"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * repeat = cleri_repeat( 10 | 0, // gid, not used in this example 11 | cleri_keyword(0, "ni", 0), // repeated element 12 | 0, // min n times 13 | 0); // max n times (0 for unlimited) 14 | 15 | /* create grammar */ 16 | cleri_grammar_t * grammar = cleri_grammar(repeat, NULL); 17 | 18 | /* parse some test string */ 19 | cleri_parse_t * pr = cleri_parse(grammar, TestRepeat); 20 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestRepeat); 21 | 22 | /* cleanup */ 23 | cleri_parse_free(pr); 24 | cleri_grammar_free(grammar); 25 | 26 | return 0; 27 | } 28 | -------------------------------------------------------------------------------- /examples/sequence/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestSequence = "Tic Tac Toe"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * sequence = cleri_sequence( 10 | 0, // gid, not used in the example 11 | 3, // number of elements 12 | cleri_keyword(0, "Tic", 0), // first element 13 | cleri_keyword(0, "Tac", 0), // second element 14 | cleri_keyword(0, "Toe", 0)); // third element 15 | 16 | /* create grammar */ 17 | cleri_grammar_t * grammar = cleri_grammar(sequence, NULL); 18 | 19 | /* parse some test string */ 20 | cleri_parse_t * pr = cleri_parse(grammar, TestSequence); 21 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestSequence); 22 | 23 | /* cleanup */ 24 | cleri_parse_free(pr); 25 | cleri_grammar_free(grammar); 26 | 27 | return 0; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /examples/token/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestToken = "ni-ni - ni- ni -ni"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * token = cleri_token( 10 | 0, // gid, not used in this example 11 | "-"); // token string (dash) 12 | 13 | cleri_t * ni = cleri_keyword(0, "ni", 0); 14 | cleri_t * list = cleri_list(0, ni, token, 0, 0, 0); 15 | 16 | /* create grammar */ 17 | cleri_grammar_t * grammar = cleri_grammar(list, NULL); 18 | 19 | /* parse some test string */ 20 | cleri_parse_t * pr = cleri_parse(grammar, TestToken); 21 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestToken); 22 | 23 | /* cleanup */ 24 | cleri_parse_free(pr); 25 | cleri_grammar_free(grammar); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /examples/tokens/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | const char * TestTokens = "ni + ni -= ni - ni"; 5 | 6 | int main(void) 7 | { 8 | /* define grammar */ 9 | cleri_t * tokens = cleri_tokens( 10 | 0, // gid, not used in this example 11 | "+ - -="); // tokens string '+', '-' and '-=' 12 | 13 | cleri_t * ni = cleri_keyword(0, "ni", 0); 14 | cleri_t * list = cleri_list(0, ni, tokens, 0, 0, 0); 15 | 16 | /* create grammar */ 17 | cleri_grammar_t * grammar = cleri_grammar(list, NULL); 18 | 19 | /* parse some test string */ 20 | cleri_parse_t * pr = cleri_parse(grammar, TestTokens); 21 | printf("Test: %s, '%s'\n", pr->is_valid ? "true" : "false", TestTokens); 22 | 23 | /* cleanup */ 24 | cleri_parse_free(pr); 25 | cleri_grammar_free(grammar); 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /inc/cleri/choice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * choice.h - this cleri element can hold other elements and the grammar 3 | * has to choose one of them. 4 | */ 5 | #ifndef CLERI_CHOICE_H_ 6 | #define CLERI_CHOICE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* typedefs */ 14 | typedef struct cleri_s cleri_t; 15 | typedef struct cleri_olist_s cleri_olist_t; 16 | typedef struct cleri_choice_s cleri_choice_t; 17 | 18 | /* public functions */ 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | cleri_t * cleri_choice(uint32_t gid, int most_greedy, size_t len, ...); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | /* structs */ 30 | struct cleri_choice_s 31 | { 32 | int most_greedy; 33 | cleri_olist_t * olist; 34 | }; 35 | 36 | #endif /* CLERI_CHOICE_H_ */ -------------------------------------------------------------------------------- /inc/cleri/cleri.h: -------------------------------------------------------------------------------- 1 | /* 2 | * cleri.h - each cleri element is a cleri object. 3 | */ 4 | #ifndef CLERI_OBJECT_H_ 5 | #define CLERI_OBJECT_H_ 6 | 7 | #ifdef __cplusplus 8 | #define cleri__malloc(__t) ((__t*)malloc(sizeof(__t))) 9 | #else 10 | #define cleri__malloc(__t) (malloc(sizeof(__t))) 11 | #endif 12 | 13 | #ifdef __cplusplus 14 | #define cleri__mallocn(__n, __t) ((__t*)malloc(__n * sizeof(__t))) 15 | #else 16 | #define cleri__mallocn(__n, __t) (malloc(__n * sizeof(__t))) 17 | #endif 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | /* typedefs */ 40 | typedef struct cleri_s cleri_t; 41 | typedef struct cleri_grammar_s cleri_grammar_t; 42 | typedef struct cleri_keyword_s cleri_keyword_t; 43 | typedef struct cleri_sequence_s cleri_sequence_t; 44 | typedef struct cleri_optional_s cleri_optional_t; 45 | typedef struct cleri_choice_s cleri_choice_t; 46 | typedef struct cleri_regex_s cleri_regex_t; 47 | typedef struct cleri_list_s cleri_list_t; 48 | typedef struct cleri_repeat_s cleri_repeat_t; 49 | typedef struct cleri_token_s cleri_token_t; 50 | typedef struct cleri_tokens_s cleri_tokens_t; 51 | typedef struct cleri_prio_s cleri_prio_t; 52 | typedef struct cleri_rule_s cleri_rule_t; 53 | typedef struct cleri_rule_store_s cleri_rule_store_t; 54 | typedef struct cleri_node_s cleri_node_t; 55 | typedef struct cleri_parse_s cleri_parse_t; 56 | typedef struct cleri_ref_s cleri_ref_t; 57 | typedef struct cleri_s cleri_t; 58 | typedef struct cleri_dup_s cleri_dup_t; 59 | 60 | typedef union cleri_u cleri_via_t; 61 | 62 | typedef void (*cleri_free_object_t)(cleri_t *); 63 | typedef cleri_node_t * (*cleri_parse_object_t)( 64 | cleri_parse_t *, 65 | cleri_node_t *, 66 | cleri_t *, 67 | cleri_rule_store_t *); 68 | 69 | /* enums */ 70 | typedef enum cleri_e { 71 | CLERI_TP_SEQUENCE, 72 | CLERI_TP_OPTIONAL, 73 | CLERI_TP_CHOICE, 74 | CLERI_TP_LIST, 75 | CLERI_TP_REPEAT, 76 | CLERI_TP_PRIO, 77 | CLERI_TP_RULE, 78 | CLERI_TP_THIS, 79 | /* all items after this will not get children */ 80 | CLERI_TP_KEYWORD, 81 | CLERI_TP_TOKEN, 82 | CLERI_TP_TOKENS, 83 | CLERI_TP_REGEX, 84 | CLERI_TP_REF, 85 | CLERI_TP_END_OF_STATEMENT 86 | } cleri_tp; 87 | 88 | /* unions */ 89 | union cleri_u 90 | { 91 | cleri_keyword_t * keyword; 92 | cleri_sequence_t * sequence; 93 | cleri_optional_t * optional; 94 | cleri_choice_t * choice; 95 | cleri_regex_t * regex; 96 | cleri_list_t * list; 97 | cleri_repeat_t * repeat; 98 | cleri_token_t * token; 99 | cleri_tokens_t * tokens; 100 | cleri_prio_t * prio; 101 | cleri_rule_t * rule; 102 | cleri_dup_t * dup; 103 | void * dummy; /* place holder */ 104 | }; 105 | 106 | /* public functions */ 107 | #ifdef __cplusplus 108 | extern "C" { 109 | #endif 110 | 111 | cleri_t * cleri_new( 112 | uint32_t gid, 113 | cleri_tp tp, 114 | cleri_free_object_t free_object, 115 | cleri_parse_object_t parse_object); 116 | void cleri_incref(cleri_t * cl_object); 117 | void cleri_decref(cleri_t * cl_object); 118 | int cleri_free(cleri_t * cl_object); 119 | 120 | #ifdef __cplusplus 121 | } 122 | #endif 123 | 124 | /* fixed end of statement object */ 125 | extern cleri_t * CLERI_END_OF_STATEMENT; 126 | 127 | /* structs */ 128 | 129 | #define CLERI_OBJECT_FIELDS \ 130 | uint32_t gid; \ 131 | uint32_t ref; \ 132 | cleri_free_object_t free_object; \ 133 | cleri_parse_object_t parse_object; \ 134 | cleri_tp tp; \ 135 | cleri_via_t via; 136 | 137 | struct cleri_s 138 | { 139 | CLERI_OBJECT_FIELDS 140 | }; 141 | 142 | struct cleri_dup_s 143 | { 144 | CLERI_OBJECT_FIELDS 145 | cleri_t * dup; 146 | }; 147 | 148 | #endif /* CLERI_OBJECT_H_ */ -------------------------------------------------------------------------------- /inc/cleri/dup.h: -------------------------------------------------------------------------------- 1 | /* 2 | * dup.h - this cleri element can be used to duplicate an element. 3 | */ 4 | #ifndef CLERI_DUP_H_ 5 | #define CLERI_DUP_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /* typedefs */ 12 | typedef struct cleri_s cleri_t; 13 | typedef struct cleri_dup_s cleri_dup_t; 14 | 15 | /* public functions */ 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | cleri_t * cleri_dup(uint32_t gid, cleri_t * cl_obj); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | /* structs */ 27 | // cleri_dup_t is defined in cleri.h 28 | 29 | #endif /* CLERI_DUP_H_ */ -------------------------------------------------------------------------------- /inc/cleri/expecting.h: -------------------------------------------------------------------------------- 1 | /* 2 | * expecting.h - holds elements which the grammar expects at one position. 3 | * this can be used for suggestions. 4 | */ 5 | #ifndef CLERI_EXPECTING_H_ 6 | #define CLERI_EXPECTING_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define CLERI__EXP_MODE_OPTIONAL 0 14 | #define CLERI__EXP_MODE_REQUIRED 1 15 | 16 | /* typedefs */ 17 | typedef struct cleri_s cleri_t; 18 | typedef struct cleri_olist_s cleri_olist_t; 19 | typedef struct cleri_exp_modes_s cleri_exp_modes_t; 20 | typedef struct cleri_expecting_s cleri_expecting_t; 21 | 22 | /* private functions */ 23 | cleri_expecting_t * cleri__expecting_new(const char * str, int flags); 24 | int cleri__expecting_update( 25 | cleri_expecting_t * expecting, 26 | cleri_t * cl_obj, 27 | const char * str); 28 | int cleri__expecting_set_mode( 29 | cleri_expecting_t * expecting, 30 | const char * str, 31 | int mode); 32 | void cleri__expecting_free(cleri_expecting_t * expecting); 33 | void cleri__expecting_combine(cleri_expecting_t * expecting); 34 | 35 | /* structs */ 36 | struct cleri_exp_modes_s 37 | { 38 | int mode; 39 | const char * str; 40 | cleri_exp_modes_t * next; 41 | }; 42 | 43 | struct cleri_expecting_s 44 | { 45 | const char * str; 46 | cleri_olist_t * required; 47 | cleri_olist_t * optional; 48 | cleri_exp_modes_t * modes; 49 | }; 50 | 51 | #endif /* CLERI_EXPECTING_H_ */ -------------------------------------------------------------------------------- /inc/cleri/grammar.h: -------------------------------------------------------------------------------- 1 | /* 2 | * grammar.h - this should contain the 'start' or your grammar. 3 | */ 4 | #ifndef CLERI_GRAMMAR_H_ 5 | #define CLERI_GRAMMAR_H_ 6 | 7 | #define PCRE2_CODE_UNIT_WIDTH 8 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #define CLERI_DEFAULT_RE_KEYWORDS "^\\w+" 14 | 15 | /* typedefs */ 16 | typedef struct cleri_s cleri_t; 17 | typedef struct cleri_grammar_s cleri_grammar_t; 18 | 19 | /* public functions */ 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | cleri_grammar_t * cleri_grammar(cleri_t * start, const char * re_keywords); 25 | void cleri_grammar_free(cleri_grammar_t * grammar); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | /* structs */ 32 | struct cleri_grammar_s 33 | { 34 | cleri_t * start; 35 | pcre2_code * re_keywords; 36 | pcre2_match_data * match_data; 37 | }; 38 | 39 | #endif /* CLERI_GRAMMAR_H_ */ -------------------------------------------------------------------------------- /inc/cleri/keyword.h: -------------------------------------------------------------------------------- 1 | /* 2 | * keyword.h - cleri keyword element. 3 | */ 4 | #ifndef CLERI_KEYWORD_H_ 5 | #define CLERI_KEYWORD_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /* typedefs */ 12 | typedef struct cleri_s cleri_t; 13 | typedef struct cleri_keyword_s cleri_keyword_t; 14 | 15 | /* public functions */ 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | cleri_t * cleri_keyword(uint32_t gid, const char * keyword, int ign_case); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | /* structs */ 26 | struct cleri_keyword_s 27 | { 28 | const char * keyword; 29 | int ign_case; 30 | size_t len; 31 | }; 32 | 33 | #endif /* CLERI_KEYWORD_H_ */ -------------------------------------------------------------------------------- /inc/cleri/kwcache.h: -------------------------------------------------------------------------------- 1 | /* 2 | * kwcache.h - holds keyword regular expression result while parsing. 3 | */ 4 | #ifndef CLERI_KWCACHE_H_ 5 | #define CLERI_KWCACHE_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /* typedefs */ 12 | typedef struct cleri_parse_s cleri_parse_t; 13 | 14 | /* private functions */ 15 | uint8_t * cleri__kwcache_new(const char * str); 16 | uint8_t cleri__kwcache_match(cleri_parse_t * pr, const char * str); 17 | void cleri__kwcache_free(uint8_t * kwcache); 18 | 19 | #endif /* CLERI_KWCACHE_H_ */ 20 | 21 | -------------------------------------------------------------------------------- /inc/cleri/list.h: -------------------------------------------------------------------------------- 1 | /* 2 | * list.h - cleri list element. 3 | */ 4 | #ifndef CLERI_LIST_H_ 5 | #define CLERI_LIST_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* typedefs */ 13 | typedef struct cleri_s cleri_t; 14 | typedef struct cleri_list_s cleri_list_t; 15 | 16 | /* public functions */ 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | cleri_t * cleri_list( 22 | uint32_t gid, 23 | cleri_t * cl_obj, 24 | cleri_t * delimiter, 25 | size_t min, 26 | size_t max, 27 | int opt_closing); 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | 33 | /* structs */ 34 | struct cleri_list_s 35 | { 36 | cleri_t * cl_obj; 37 | cleri_t * delimiter; 38 | size_t min; 39 | size_t max; 40 | int opt_closing; 41 | }; 42 | 43 | #endif /* CLERI_LIST_H_ */ -------------------------------------------------------------------------------- /inc/cleri/node.h: -------------------------------------------------------------------------------- 1 | /* 2 | * node.h - node is created while parsing a string. a node old the result 3 | * for one element. 4 | */ 5 | #ifndef CLERI_NODE_H_ 6 | #define CLERI_NODE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | /* typedefs */ 14 | typedef struct cleri_s cleri_t; 15 | typedef struct cleri_node_s cleri_node_t; 16 | 17 | /* public macro function */ 18 | #define cleri_node_has_children(__node) ((__node)->children != NULL) 19 | 20 | /* private functions */ 21 | cleri_node_t * cleri__node_new(cleri_t * cl_obj, const char * str, size_t len); 22 | cleri_node_t * cleri__node_dup(cleri_node_t * node); 23 | void cleri__node_free(cleri_node_t * node); 24 | 25 | /* private use as empty node */ 26 | extern cleri_node_t * CLERI_EMPTY_NODE; 27 | 28 | /* structs */ 29 | struct cleri_node_s 30 | { 31 | uint32_t ref; 32 | uint32_t len; 33 | 34 | const char * str; 35 | cleri_t * cl_obj; 36 | cleri_node_t * children; 37 | cleri_node_t * next; 38 | 39 | void * data; /* free to use by the user */ 40 | }; 41 | 42 | static inline void cleri__node_add(cleri_node_t * parent, cleri_node_t * node) 43 | { 44 | cleri_node_t ** nodeaddr = parent->data; 45 | *nodeaddr = node; 46 | parent->data = &node->next; 47 | } 48 | 49 | #endif /* CLERI_NODE_H_ */ -------------------------------------------------------------------------------- /inc/cleri/olist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * olist.h - linked list for keeping cleri objects. 3 | */ 4 | #ifndef CLERI_OLIST_H_ 5 | #define CLERI_OLIST_H_ 6 | 7 | #include 8 | 9 | /* typedefs */ 10 | typedef struct cleri_s cleri_t; 11 | typedef struct cleri_olist_s cleri_olist_t; 12 | typedef struct cleri_olist_s cleri_olist_t; 13 | 14 | /* private functions */ 15 | cleri_olist_t * cleri__olist_new(void); 16 | int cleri__olist_append(cleri_olist_t * olist, cleri_t * cl_object); 17 | int cleri__olist_append_nref(cleri_olist_t * olist, cleri_t * cl_object); 18 | void cleri__olist_free(cleri_olist_t * olist); 19 | void cleri__olist_empty(cleri_olist_t * olist); 20 | void cleri__olist_cancel(cleri_olist_t * olist); 21 | void cleri__olist_unique(cleri_olist_t * olist); 22 | 23 | /* structs */ 24 | struct cleri_olist_s 25 | { 26 | cleri_t * cl_obj; 27 | cleri_olist_t * next; 28 | }; 29 | 30 | #endif /* CLERI_OLIST_H_ */ -------------------------------------------------------------------------------- /inc/cleri/optional.h: -------------------------------------------------------------------------------- 1 | /* 2 | * optional.h - cleri optional element. 3 | */ 4 | #ifndef CLERI_OPTIONAL_H_ 5 | #define CLERI_OPTIONAL_H_ 6 | 7 | #include 8 | #include 9 | 10 | /* typedefs */ 11 | typedef struct cleri_s cleri_t; 12 | typedef struct cleri_optional_s cleri_optional_t; 13 | 14 | /* public functions */ 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | cleri_t * cleri_optional(uint32_t gid, cleri_t * cl_obj); 20 | 21 | #ifdef __cplusplus 22 | } 23 | #endif 24 | 25 | /* structs */ 26 | struct cleri_optional_s 27 | { 28 | cleri_t * cl_obj; 29 | }; 30 | 31 | #endif /* CLERI_OPTIONAL_H_ */ -------------------------------------------------------------------------------- /inc/cleri/parse.h: -------------------------------------------------------------------------------- 1 | /* 2 | * parse.h - this contains everything for parsing a string to a grammar. 3 | */ 4 | #ifndef CLERI_PARSE_H_ 5 | #define CLERI_PARSE_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifndef MAX_RECURSION_DEPTH 17 | #define MAX_RECURSION_DEPTH 500 18 | #endif 19 | 20 | enum 21 | { 22 | CLERI_FLAG_EXPECTING_DISABLED =1<<0, 23 | CLERI_FLAG_EXCLUDE_OPTIONAL =1<<1, 24 | CLERI_FLAG_EXCLUDE_FM_CHOICE =1<<2, 25 | CLERI_FLAG_EXCLUDE_RULE_THIS =1<<3, 26 | }; 27 | 28 | /* typedefs */ 29 | typedef struct cleri_s cleri_t; 30 | typedef struct cleri_grammar_s cleri_grammar_t; 31 | typedef struct cleri_node_s cleri_node_t; 32 | typedef struct cleri_expecting_s cleri_expecting_t; 33 | typedef struct cleri_rule_store_s cleri_rule_store_t; 34 | typedef struct cleri_parse_s cleri_parse_t; 35 | typedef const char * (cleri_translate_t)(cleri_t *); 36 | 37 | /* public functions */ 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | static inline cleri_parse_t * cleri_parse( 43 | cleri_grammar_t * grammar, 44 | const char * str); 45 | cleri_parse_t * cleri_parse2( 46 | cleri_grammar_t * grammar, 47 | const char * str, 48 | int flags); 49 | void cleri_parse_free(cleri_parse_t * pr); 50 | void cleri_parse_expect_start(cleri_parse_t * pr); 51 | int cleri_parse_strn( 52 | char * s, 53 | size_t n, 54 | cleri_parse_t * pr, 55 | cleri_translate_t * translate); 56 | 57 | #ifdef __cplusplus 58 | } 59 | #endif 60 | 61 | /* private functions */ 62 | cleri_node_t * cleri__parse_walk( 63 | cleri_parse_t * pr, 64 | cleri_node_t * parent, 65 | cleri_t * cl_obj, 66 | cleri_rule_store_t * rule, 67 | int mode); 68 | 69 | /* structs */ 70 | struct cleri_parse_s 71 | { 72 | int is_valid; 73 | int flags; 74 | size_t pos; 75 | const char * str; 76 | cleri_node_t * tree; 77 | const cleri_olist_t * expect; 78 | cleri_expecting_t * expecting; 79 | pcre2_code * re_keywords; 80 | pcre2_match_data * match_data; 81 | uint8_t * kwcache; 82 | }; 83 | 84 | static inline cleri_parse_t * cleri_parse( 85 | cleri_grammar_t * grammar, 86 | const char * str) 87 | { 88 | return cleri_parse2(grammar, str, 0); 89 | } 90 | 91 | #endif /* CLERI_PARSE_H_ */ 92 | -------------------------------------------------------------------------------- /inc/cleri/prio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * prio.h - cleri prio element. (this element create a cleri rule object 3 | * holding this prio element) 4 | */ 5 | #ifndef CLERI_PRIO_H_ 6 | #define CLERI_PRIO_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* typedefs */ 15 | typedef struct cleri_s cleri_t; 16 | typedef struct cleri_olist_s cleri_olist_t; 17 | typedef struct cleri_prio_s cleri_prio_t; 18 | 19 | /* public functions */ 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | cleri_t * cleri_prio(uint32_t gid, size_t len, ...); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | /* structs */ 31 | struct cleri_prio_s 32 | { 33 | cleri_olist_t * olist; 34 | }; 35 | 36 | #endif /* CLERI_PRIO_H_ */ -------------------------------------------------------------------------------- /inc/cleri/ref.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ref.h - cleri ref element 3 | */ 4 | #ifndef CLERI_REF_H_ 5 | #define CLERI_REF_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | /* typedefs */ 12 | typedef struct cleri_s cleri_t; 13 | 14 | /* public functions */ 15 | #ifdef __cplusplus 16 | extern "C" { 17 | #endif 18 | 19 | cleri_t * cleri_ref(void); 20 | void cleri_ref_set(cleri_t * ref, cleri_t * cl_obj); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif /* CLERI_REF_H_ */ -------------------------------------------------------------------------------- /inc/cleri/regex.h: -------------------------------------------------------------------------------- 1 | /* 2 | * regex.h - cleri regular expression element. 3 | */ 4 | #ifndef CLERI_REGEX_H_ 5 | #define CLERI_REGEX_H_ 6 | 7 | #define PCRE2_CODE_UNIT_WIDTH 8 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* typedefs */ 15 | typedef struct cleri_s cleri_t; 16 | typedef struct cleri_regex_s cleri_regex_t; 17 | 18 | /* public functions */ 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | cleri_t * cleri_regex(uint32_t gid, const char * pattern); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | /* structs */ 30 | struct cleri_regex_s 31 | { 32 | pcre2_code * regex; 33 | pcre2_match_data * match_data; 34 | }; 35 | 36 | #endif /* CLERI_REGEX_H_ */ -------------------------------------------------------------------------------- /inc/cleri/repeat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * repeat.h - cleri regular repeat element. 3 | */ 4 | #ifndef CLERI_REPEAT_H_ 5 | #define CLERI_REPEAT_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* typedefs */ 13 | typedef struct cleri_s cleri_t; 14 | typedef struct cleri_repeat_s cleri_repeat_t; 15 | 16 | /* public functions */ 17 | #ifdef __cplusplus 18 | extern "C" { 19 | #endif 20 | 21 | cleri_t * cleri_repeat(uint32_t gid, cleri_t * cl_obj, size_t min, size_t max); 22 | 23 | #ifdef __cplusplus 24 | } 25 | #endif 26 | 27 | /* structs */ 28 | struct cleri_repeat_s 29 | { 30 | cleri_t * cl_obj; 31 | size_t min; 32 | size_t max; 33 | }; 34 | 35 | #endif /* CLERI_REPEAT_H_ */ -------------------------------------------------------------------------------- /inc/cleri/rule.h: -------------------------------------------------------------------------------- 1 | /* 2 | * rule.h - cleri regular rule element. (do not directly use this element but 3 | * create a 'prio' instead which will be wrapped by a rule element) 4 | */ 5 | #ifndef CLERI_RULE_H_ 6 | #define CLERI_RULE_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* typedefs */ 15 | typedef struct cleri_s cleri_t; 16 | typedef struct cleri_node_s cleri_node_t; 17 | typedef struct cleri_rule_tested_s cleri_rule_tested_t; 18 | typedef struct cleri_rule_store_s cleri_rule_store_t; 19 | typedef struct cleri_rule_s cleri_rule_t; 20 | 21 | /* enums */ 22 | typedef enum cleri_rule_test_e 23 | { 24 | CLERI_RULE_ERROR=-1, 25 | CLERI_RULE_FALSE, 26 | CLERI_RULE_TRUE 27 | } cleri_rule_test_t; 28 | 29 | /* private functions */ 30 | cleri_t * cleri__rule(uint32_t gid, cleri_t * cl_obj); 31 | cleri_rule_test_t cleri__rule_init( 32 | cleri_rule_tested_t ** target, 33 | cleri_rule_tested_t * tested, 34 | const char * str); 35 | 36 | /* structs */ 37 | struct cleri_rule_tested_s 38 | { 39 | const char * str; 40 | cleri_node_t * node; 41 | cleri_rule_tested_t * next; 42 | }; 43 | 44 | struct cleri_rule_store_s 45 | { 46 | cleri_rule_tested_t tested; 47 | cleri_t * root_obj; 48 | size_t depth; 49 | }; 50 | 51 | struct cleri_rule_s 52 | { 53 | cleri_t * cl_obj; 54 | }; 55 | 56 | #endif /* CLERI_RULE_H_ */ -------------------------------------------------------------------------------- /inc/cleri/sequence.h: -------------------------------------------------------------------------------- 1 | /* 2 | * sequence.h - cleri sequence element. 3 | */ 4 | #ifndef CLERI_SEQUENCE_H_ 5 | #define CLERI_SEQUENCE_H_ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* typedefs */ 13 | typedef struct cleri_s cleri_t; 14 | typedef struct cleri_olist_s cleri_olist_t; 15 | typedef struct cleri_sequence_s cleri_sequence_t; 16 | 17 | /* public functions */ 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | cleri_t * cleri_sequence(uint32_t gid, size_t len, ...); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | /* structs */ 29 | struct cleri_sequence_s 30 | { 31 | cleri_olist_t * olist; 32 | }; 33 | 34 | #endif /* CLERI_SEQUENCE_H_ */ -------------------------------------------------------------------------------- /inc/cleri/this.h: -------------------------------------------------------------------------------- 1 | /* 2 | * this.h - cleri THIS element. there should be only one single instance 3 | * of this which can even be shared over different grammars. 4 | * Always use this element using its constant CLERI_THIS and 5 | * somewhere within a prio element. 6 | */ 7 | #ifndef CLERI_THIS_H_ 8 | #define CLERI_THIS_H_ 9 | 10 | #include 11 | #include 12 | 13 | /* typedefs */ 14 | typedef struct cleri_s cleri_t; 15 | 16 | /* public THIS */ 17 | extern cleri_t * CLERI_THIS; 18 | 19 | #endif /* CLERI_THIS_H_ */ -------------------------------------------------------------------------------- /inc/cleri/token.h: -------------------------------------------------------------------------------- 1 | /* 2 | * token.h - cleri token element. note that one single char will parse 3 | * slightly faster compared to tokens containing more characters. 4 | * (be careful a token should not match the keyword regular 5 | * expression) 6 | */ 7 | #ifndef CLERI_TOKEN_H_ 8 | #define CLERI_TOKEN_H_ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /* typedefs */ 15 | typedef struct cleri_s cleri_t; 16 | typedef struct cleri_token_s cleri_token_t; 17 | 18 | /* public functions */ 19 | #ifdef __cplusplus 20 | extern "C" { 21 | #endif 22 | 23 | cleri_t * cleri_token(uint32_t gid, const char * token); 24 | 25 | #ifdef __cplusplus 26 | } 27 | #endif 28 | 29 | /* structs */ 30 | struct cleri_token_s 31 | { 32 | const char * token; 33 | size_t len; 34 | }; 35 | 36 | #endif /* CLERI_TOKEN_H_ */ 37 | -------------------------------------------------------------------------------- /inc/cleri/tokens.h: -------------------------------------------------------------------------------- 1 | /* 2 | * tokens.h - cleri tokens element. (like token but can contain more tokens 3 | * in one element) 4 | */ 5 | #ifndef CLERI_TOKENS_H_ 6 | #define CLERI_TOKENS_H_ 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /* typedefs */ 13 | typedef struct cleri_s cleri_t; 14 | typedef struct cleri_tlist_s cleri_tlist_t; 15 | typedef struct cleri_tokens_s cleri_tokens_t; 16 | 17 | /* public functions */ 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | cleri_t * cleri_tokens(uint32_t gid, const char * tokens); 23 | 24 | #ifdef __cplusplus 25 | } 26 | #endif 27 | 28 | /* structs */ 29 | struct cleri_tlist_s 30 | { 31 | const char * token; 32 | size_t len; 33 | cleri_tlist_t * next; 34 | }; 35 | 36 | struct cleri_tokens_s 37 | { 38 | char * tokens; 39 | char * spaced; 40 | cleri_tlist_t * tlist; 41 | }; 42 | 43 | #endif /* CLERI_TOKENS_H_ */ -------------------------------------------------------------------------------- /inc/cleri/version.h: -------------------------------------------------------------------------------- 1 | /* 2 | * version.h - cleri version information. 3 | */ 4 | #ifndef CLERI_VERSION_H_ 5 | #define CLERI_VERSION_H_ 6 | 7 | #define CLERI_VERSION_MAJOR 1 8 | #define CLERI_VERSION_MINOR 0 9 | #define CLERI_VERSION_PATCH 2 10 | 11 | #define VERSION__STRINGIFY(num) #num 12 | #define VERSION___STR(major,minor,patch) \ 13 | VERSION__STRINGIFY(major) "." \ 14 | VERSION__STRINGIFY(minor) "." \ 15 | VERSION__STRINGIFY(patch) 16 | /* end helpers */ 17 | 18 | /* start auto generated from above */ 19 | #define LIBCLERI_VERSION VERSION___STR( \ 20 | CLERI_VERSION_MAJOR, \ 21 | CLERI_VERSION_MINOR, \ 22 | CLERI_VERSION_PATCH) 23 | 24 | /* public funtion */ 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | const char * cleri_version(void); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* CLERI_VERSION_H_ */ 36 | -------------------------------------------------------------------------------- /makefile.init: -------------------------------------------------------------------------------- 1 | MAJOR := 1 2 | MINOR := 0 3 | PATCH := 2 4 | VERSION := $(MAJOR).$(MINOR).$(PATCH) 5 | -------------------------------------------------------------------------------- /makefile.targets: -------------------------------------------------------------------------------- 1 | .PHONY: install 2 | install: 3 | @mkdir $(INSTALL_PATH)/include/cleri 4 | @cp ../inc/cleri/*.h $(INSTALL_PATH)/include/cleri/ 5 | @cp $(FN) $(INSTALL_PATH)/lib/$(FN).$(VERSION) 6 | @ln -s $(INSTALL_PATH)/lib/$(FN).$(VERSION) $(INSTALL_PATH)/lib/$(FN).$(MAJOR) 7 | @ln -s $(INSTALL_PATH)/lib/$(FN).$(VERSION) $(INSTALL_PATH)/lib/$(FN) 8 | 9 | 10 | .PHONY: uninstall 11 | uninstall: 12 | @rm -f $(INSTALL_PATH)/include/cleri/*.h 13 | @rm -r -f $(INSTALL_PATH)/include/cleri/ 14 | @rm -f $(INSTALL_PATH)/lib/$(FN) 15 | @rm -f $(INSTALL_PATH)/lib/$(FN).$(MAJOR) 16 | @rm -f $(INSTALL_PATH)/lib/$(FN).$(VERSION) 17 | 18 | -------------------------------------------------------------------------------- /src/choice.c: -------------------------------------------------------------------------------- 1 | /* 2 | * choice.c - this cleri element can hold other elements and the grammar 3 | * has to choose one of them. 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | static void choice__free(cleri_t * cl_object); 12 | 13 | static cleri_node_t * choice__parse( 14 | cleri_parse_t * pr, 15 | cleri_node_t * parent, 16 | cleri_t * cl_obj, 17 | cleri_rule_store_t * rule); 18 | 19 | static cleri_node_t * choice__parse_most_greedy( 20 | cleri_parse_t * pr, 21 | cleri_node_t * parent, 22 | cleri_t * cl_obj, 23 | cleri_rule_store_t * rule); 24 | 25 | static cleri_node_t * choice__parse_first_match( 26 | cleri_parse_t * pr, 27 | cleri_node_t * parent, 28 | cleri_t * cl_obj, 29 | cleri_rule_store_t * rule); 30 | 31 | /* 32 | * Returns NULL in case an error has occurred. 33 | */ 34 | cleri_t * cleri_choice(uint32_t gid, int most_greedy, size_t len, ...) 35 | { 36 | va_list ap; 37 | 38 | cleri_t * cl_object = cleri_new( 39 | gid, 40 | CLERI_TP_CHOICE, 41 | &choice__free, 42 | &choice__parse); 43 | 44 | if (cl_object == NULL) 45 | { 46 | return NULL; 47 | } 48 | 49 | cl_object->via.choice = cleri__malloc(cleri_choice_t); 50 | 51 | if (cl_object->via.choice == NULL) 52 | { 53 | free(cl_object); 54 | return NULL; 55 | } 56 | 57 | cl_object->via.choice->most_greedy = most_greedy; 58 | cl_object->via.choice->olist = cleri__olist_new(); 59 | 60 | if (cl_object->via.choice->olist == NULL) 61 | { 62 | cleri_free(cl_object); 63 | return NULL; 64 | } 65 | 66 | va_start(ap, len); 67 | while(len--) 68 | { 69 | if (cleri__olist_append( 70 | cl_object->via.choice->olist, 71 | va_arg(ap, cleri_t *))) 72 | { 73 | cleri__olist_cancel(cl_object->via.choice->olist); 74 | cleri_free(cl_object); 75 | cl_object = NULL; 76 | break; 77 | } 78 | } 79 | va_end(ap); 80 | 81 | return cl_object; 82 | } 83 | 84 | /* 85 | * Destroy choice object. 86 | */ 87 | static void choice__free(cleri_t * cl_object) 88 | { 89 | cleri__olist_free(cl_object->via.choice->olist); 90 | free(cl_object->via.choice); 91 | } 92 | 93 | /* 94 | * Returns a node or NULL. 95 | */ 96 | static cleri_node_t * choice__parse( 97 | cleri_parse_t * pr, 98 | cleri_node_t * parent, 99 | cleri_t * cl_obj, 100 | cleri_rule_store_t * rule) 101 | { 102 | return (cl_obj->via.choice->most_greedy) ? 103 | choice__parse_most_greedy(pr, parent, cl_obj, rule) : 104 | choice__parse_first_match(pr, parent, cl_obj, rule); 105 | } 106 | 107 | /* 108 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 109 | */ 110 | static cleri_node_t * choice__parse_most_greedy( 111 | cleri_parse_t * pr, 112 | cleri_node_t * parent, 113 | cleri_t * cl_obj, 114 | cleri_rule_store_t * rule) 115 | { 116 | cleri_olist_t * olist; 117 | cleri_node_t * node; 118 | cleri_node_t * rnode; 119 | cleri_node_t * mg_node = NULL; 120 | const char * str = parent->str + parent->len; 121 | 122 | olist = cl_obj->via.choice->olist; 123 | while (olist != NULL) 124 | { 125 | if ((node = cleri__node_new(cl_obj, str, 0)) == NULL) 126 | { 127 | pr->is_valid = -1; 128 | return NULL; 129 | } 130 | rnode = cleri__parse_walk( 131 | pr, 132 | node, 133 | olist->cl_obj, 134 | rule, 135 | CLERI__EXP_MODE_REQUIRED); 136 | if (rnode != NULL && (mg_node == NULL || node->len > mg_node->len)) 137 | { 138 | cleri__node_free(mg_node); 139 | mg_node = node; 140 | } 141 | else 142 | { 143 | cleri__node_free(node); 144 | } 145 | olist = olist->next; 146 | } 147 | if (mg_node != NULL) 148 | { 149 | parent->len += mg_node->len; 150 | cleri__node_add(parent, mg_node); 151 | } 152 | return mg_node; 153 | } 154 | 155 | /* 156 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 157 | */ 158 | static cleri_node_t * choice__parse_first_match( 159 | cleri_parse_t * pr, 160 | cleri_node_t * parent, 161 | cleri_t * cl_obj, 162 | cleri_rule_store_t * rule) 163 | { 164 | cleri_olist_t * olist; 165 | cleri_node_t * node; 166 | cleri_node_t * rnode; 167 | 168 | olist = cl_obj->via.choice->olist; 169 | 170 | if ((pr->flags & CLERI_FLAG_EXCLUDE_FM_CHOICE) && !cl_obj->gid) 171 | { 172 | while (olist != NULL) 173 | { 174 | node = cleri__parse_walk( 175 | pr, 176 | parent, 177 | olist->cl_obj, 178 | rule, 179 | CLERI__EXP_MODE_REQUIRED); 180 | if (node != NULL) 181 | { 182 | return node; 183 | } 184 | olist = olist->next; 185 | } 186 | return NULL; 187 | } 188 | 189 | node = cleri__node_new(cl_obj, parent->str + parent->len, 0); 190 | if (node == NULL) 191 | { 192 | pr->is_valid = -1; 193 | return NULL; 194 | } 195 | while (olist != NULL) 196 | { 197 | rnode = cleri__parse_walk( 198 | pr, 199 | node, 200 | olist->cl_obj, 201 | rule, 202 | CLERI__EXP_MODE_REQUIRED); 203 | if (rnode != NULL) 204 | { 205 | parent->len += node->len; 206 | cleri__node_add(parent, node); 207 | return node; 208 | } 209 | olist = olist->next; 210 | } 211 | cleri__node_free(node); 212 | return NULL; 213 | } 214 | -------------------------------------------------------------------------------- /src/cleri.c: -------------------------------------------------------------------------------- 1 | /* 2 | * cleri.c - each cleri element is a cleri object. 3 | */ 4 | #include 5 | #include 6 | 7 | static cleri_t end_of_statement = { 8 | .gid=0, 9 | .ref=1, 10 | .free_object=NULL, 11 | .parse_object=NULL, 12 | .tp=CLERI_TP_END_OF_STATEMENT, 13 | .via={.dummy=NULL}}; 14 | cleri_t * CLERI_END_OF_STATEMENT = &end_of_statement; 15 | 16 | /* 17 | * Returns NULL in case an error has occurred. 18 | */ 19 | cleri_t * cleri_new( 20 | uint32_t gid, 21 | cleri_tp tp, 22 | cleri_free_object_t free_object, 23 | cleri_parse_object_t parse_object) 24 | { 25 | cleri_t * cl_object = cleri__malloc(cleri_t); 26 | if (cl_object != NULL) 27 | { 28 | cl_object->gid = gid; 29 | cl_object->tp = tp; 30 | cl_object->ref = 1; 31 | cl_object->via.dummy = NULL; 32 | cl_object->free_object = free_object; 33 | cl_object->parse_object = parse_object; 34 | } 35 | return cl_object; 36 | } 37 | 38 | /* 39 | * Increment reference counter on cleri object. 40 | */ 41 | void cleri_incref(cleri_t * cl_object) 42 | { 43 | cl_object->ref++; 44 | } 45 | 46 | /* 47 | * Decrement reference counter. 48 | * If no references are left the object is destoryed. (never the element) 49 | */ 50 | void cleri_decref(cleri_t * cl_object) 51 | { 52 | if (!--cl_object->ref) 53 | { 54 | free(cl_object); 55 | } 56 | } 57 | 58 | /* 59 | * Recursive cleanup an object including the element. 60 | * If there are still references left, then only the element but not the object 61 | * is not destroyed and this function will return -1. If successful 62 | * cleaned the return value is 0. 63 | */ 64 | int cleri_free(cleri_t * cl_object) 65 | { 66 | if (cl_object->tp == CLERI_TP_THIS) 67 | { 68 | return 0; 69 | } 70 | 71 | /* Use tp to check because we need to be sure this check validates false 72 | * before calling the free function. */ 73 | if (cl_object->tp != CLERI_TP_REF) 74 | { 75 | /* Change the type so the other are treated as references */ 76 | cl_object->tp = CLERI_TP_REF; 77 | 78 | (*cl_object->free_object)(cl_object); 79 | 80 | /* We decrement once more as soon as the element has joined at least 81 | * one other element so we don't have to run the cleanup on this 82 | * specific element. */ 83 | if (cl_object->ref > 1) 84 | { 85 | cl_object->ref--; 86 | } 87 | } 88 | 89 | if (!--cl_object->ref) 90 | { 91 | free(cl_object); 92 | return 0; 93 | } 94 | return -1; 95 | } 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /src/dup.c: -------------------------------------------------------------------------------- 1 | /* 2 | * dup.c - this cleri element can be used to duplicate an element. 3 | */ 4 | #include 5 | 6 | static void dup__free(cleri_t * cl_object); 7 | 8 | /* 9 | * Duplicate a libcleri object. 10 | * Note: a pointer to the original object->via (element) is used. 11 | */ 12 | cleri_t * cleri_dup(uint32_t gid, cleri_t * cl_obj) 13 | { 14 | cleri_dup_t * dup = cleri__malloc(cleri_dup_t); 15 | if (dup != NULL) 16 | { 17 | dup->gid = gid; 18 | dup->ref = 1; 19 | dup->tp = cl_obj->tp; 20 | dup->via = cl_obj->via; 21 | dup->free_object = &dup__free; 22 | dup->parse_object = cl_obj->parse_object; 23 | cleri_incref(cl_obj); 24 | dup->dup = cl_obj; 25 | } 26 | return (cleri_t *) dup; 27 | } 28 | 29 | static void dup__free(cleri_t * cl_object) 30 | { 31 | cleri_dup_t * dup = (cleri_dup_t *) cl_object; 32 | cleri_free(dup->dup); 33 | } 34 | -------------------------------------------------------------------------------- /src/expecting.c: -------------------------------------------------------------------------------- 1 | /* 2 | * expecting.c - holds elements which the grammar expects at one position. 3 | * this can be used for suggestions. 4 | */ 5 | #include 6 | #include 7 | 8 | 9 | static void expecting__empty(cleri_expecting_t * expecting); 10 | static int expecting__get_mode(cleri_exp_modes_t * modes, const char * str); 11 | static void expecting__shift_modes( 12 | cleri_exp_modes_t ** modes, 13 | const char * str); 14 | static void expecting__modes_free(cleri_exp_modes_t * modes); 15 | 16 | /* 17 | * Returns NULL in case an error has occurred. 18 | */ 19 | cleri_expecting_t * cleri__expecting_new(const char * str, int flags) 20 | { 21 | cleri_expecting_t * expecting = cleri__malloc(cleri_expecting_t); 22 | 23 | if (expecting != NULL) 24 | { 25 | expecting->str = str; 26 | expecting->modes = NULL; 27 | 28 | if (flags & CLERI_FLAG_EXPECTING_DISABLED) 29 | { 30 | expecting->required = NULL; 31 | expecting->optional = NULL; 32 | return expecting; 33 | } 34 | 35 | if ((expecting->required = cleri__olist_new()) == NULL) 36 | { 37 | free(expecting); 38 | return NULL; 39 | } 40 | 41 | if ((expecting->optional = cleri__olist_new()) == NULL) 42 | { 43 | free(expecting->required); 44 | free(expecting); 45 | return NULL; 46 | } 47 | 48 | } 49 | 50 | return expecting; 51 | } 52 | 53 | /* 54 | * Returns 0 if the mode is set successful and -1 if an error has occurred. 55 | */ 56 | int cleri__expecting_update( 57 | cleri_expecting_t * expecting, 58 | cleri_t * cl_obj, 59 | const char * str) 60 | { 61 | int rc = 0; 62 | if (expecting->required == NULL) 63 | { 64 | if (str > expecting->str) 65 | { 66 | expecting->str = str; 67 | } 68 | return 0; 69 | } 70 | 71 | 72 | if (str > expecting->str) 73 | { 74 | expecting__empty(expecting); 75 | expecting->str = str; 76 | expecting__shift_modes(&(expecting->modes), str); 77 | } 78 | 79 | if (expecting->str == str) 80 | { 81 | if (expecting__get_mode(expecting->modes, str)) 82 | { 83 | /* true (1) is required */ 84 | rc = cleri__olist_append_nref(expecting->required, cl_obj); 85 | } 86 | else 87 | { 88 | /* false (0) is optional */ 89 | rc = cleri__olist_append_nref(expecting->optional, cl_obj); 90 | } 91 | } 92 | 93 | return rc; 94 | } 95 | 96 | /* 97 | * Returns 0 if the mode is set successful and -1 if an error has occurred. 98 | */ 99 | int cleri__expecting_set_mode( 100 | cleri_expecting_t * expecting, 101 | const char * str, 102 | int mode) 103 | { 104 | cleri_exp_modes_t ** modes = &expecting->modes; 105 | 106 | if (expecting->required == NULL) 107 | { 108 | return 0; 109 | } 110 | 111 | for (; *modes != NULL; modes = &(*modes)->next) 112 | { 113 | if ((*modes)->str == str) 114 | { 115 | (*modes)->mode = mode && (*modes)->mode; 116 | return 0; 117 | } 118 | } 119 | 120 | *modes = cleri__malloc(cleri_exp_modes_t); 121 | 122 | if (*modes == NULL) 123 | { 124 | return -1; 125 | } 126 | 127 | (*modes)->mode = mode; 128 | (*modes)->next = NULL; 129 | (*modes)->str = str; 130 | 131 | return 0; 132 | } 133 | 134 | /* 135 | * Destroy expecting object. 136 | */ 137 | void cleri__expecting_free(cleri_expecting_t * expecting) 138 | { 139 | expecting__empty(expecting); 140 | free(expecting->required); 141 | free(expecting->optional); 142 | expecting__modes_free(expecting->modes); 143 | free(expecting); 144 | } 145 | 146 | /* 147 | * append optional to required and sets optional to NULL 148 | */ 149 | void cleri__expecting_combine(cleri_expecting_t * expecting) 150 | { 151 | cleri_olist_t * required = expecting->required; 152 | 153 | if (expecting->optional->cl_obj == NULL) 154 | { 155 | free(expecting->optional); 156 | expecting->optional = NULL; 157 | } 158 | 159 | if (required->cl_obj == NULL) 160 | { 161 | free(expecting->required); 162 | expecting->required = expecting->optional; 163 | } 164 | else 165 | { 166 | while (required->next != NULL) 167 | { 168 | required = required->next; 169 | } 170 | required->next = expecting->optional; 171 | } 172 | expecting->optional = NULL; 173 | } 174 | 175 | /* 176 | * shift from modes 177 | */ 178 | static void expecting__shift_modes( 179 | cleri_exp_modes_t ** modes, 180 | const char * str) 181 | { 182 | cleri_exp_modes_t * next; 183 | 184 | while ((*modes)->next != NULL) 185 | { 186 | if ((*modes)->str == str) 187 | { 188 | break; 189 | } 190 | next = (*modes)->next; 191 | free(*modes); 192 | *modes = next; 193 | } 194 | (*modes)->str = str; 195 | } 196 | 197 | /* 198 | * Destroy modes. 199 | */ 200 | static void expecting__modes_free(cleri_exp_modes_t * modes) 201 | { 202 | cleri_exp_modes_t * next; 203 | while (modes != NULL) 204 | { 205 | next = modes->next; 206 | free(modes); 207 | modes = next; 208 | } 209 | } 210 | 211 | /* 212 | * Return modes for a given position in str. 213 | */ 214 | static int expecting__get_mode(cleri_exp_modes_t * modes, const char * str) 215 | { 216 | for (; modes != NULL; modes = modes->next) 217 | { 218 | if (modes->str == str) 219 | { 220 | return modes->mode; 221 | } 222 | } 223 | return CLERI__EXP_MODE_REQUIRED; 224 | } 225 | 226 | /* 227 | * Empty both required and optional lists. 228 | */ 229 | static void expecting__empty(cleri_expecting_t * expecting) 230 | { 231 | cleri__olist_empty(expecting->required); 232 | cleri__olist_empty(expecting->optional); 233 | } 234 | -------------------------------------------------------------------------------- /src/grammar.c: -------------------------------------------------------------------------------- 1 | /* 2 | * grammar.c - this should contain the 'start' or your grammar. 3 | */ 4 | #define PCRE2_CODE_UNIT_WIDTH 8 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | /* 13 | * Returns a grammar object or NULL in case of an error. 14 | * 15 | * Warning: this function could write to stderr in case the re_keywords could 16 | * not be compiled. 17 | */ 18 | cleri_grammar_t * cleri_grammar(cleri_t * start, const char * re_keywords) 19 | { 20 | const char * re_kw = (re_keywords == NULL) ? 21 | CLERI_DEFAULT_RE_KEYWORDS : re_keywords; 22 | 23 | /* re_keywords should start with a ^ */ 24 | assert (re_kw[0] == '^'); 25 | 26 | if (start == NULL) 27 | { 28 | return NULL; 29 | } 30 | 31 | cleri_grammar_t * grammar = cleri__malloc(cleri_grammar_t); 32 | if (grammar == NULL) 33 | { 34 | return NULL; 35 | } 36 | 37 | int pcre_error_num; 38 | PCRE2_SIZE pcre_error_offset; 39 | 40 | grammar->re_keywords = pcre2_compile( 41 | (PCRE2_SPTR8) re_kw, 42 | PCRE2_ZERO_TERMINATED, 43 | 0, 44 | &pcre_error_num, 45 | &pcre_error_offset, 46 | NULL); 47 | if(grammar->re_keywords == NULL) 48 | { 49 | 50 | PCRE2_UCHAR buffer[256]; 51 | pcre2_get_error_message(pcre_error_num, buffer, sizeof(buffer)); 52 | /* this is critical and unexpected, memory is not cleaned */ 53 | fprintf(stderr, 54 | "error: cannot compile '%s' (%s)\n", 55 | re_kw, 56 | buffer); 57 | free(grammar); 58 | return NULL; 59 | } 60 | 61 | grammar->match_data = \ 62 | pcre2_match_data_create_from_pattern(grammar->re_keywords, NULL); 63 | 64 | if (grammar->match_data == NULL) 65 | { 66 | pcre2_code_free(grammar->re_keywords); 67 | fprintf(stderr, "error: cannot create matsch data\n"); 68 | free(grammar); 69 | return NULL; 70 | } 71 | 72 | /* bind root element and increment the reference counter */ 73 | grammar->start = start; 74 | cleri_incref(start); 75 | 76 | return grammar; 77 | } 78 | 79 | void cleri_grammar_free(cleri_grammar_t * grammar) 80 | { 81 | pcre2_match_data_free(grammar->match_data); 82 | pcre2_code_free(grammar->re_keywords); 83 | cleri_free(grammar->start); 84 | free(grammar); 85 | } 86 | -------------------------------------------------------------------------------- /src/keyword.c: -------------------------------------------------------------------------------- 1 | /* 2 | * keyword.c - cleri keyword element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static void keyword__free(cleri_t * cl_object); 10 | 11 | static cleri_node_t * keyword__parse( 12 | cleri_parse_t * pr, 13 | cleri_node_t * parent, 14 | cleri_t * cl_obj, 15 | cleri_rule_store_t * rule); 16 | 17 | /* 18 | * Keywords must match the `keyword` regular expression defined by the grammar, 19 | * and should have more than 0 and less than 255 characters. 20 | * 21 | * Returns NULL in case an error has occurred. 22 | */ 23 | cleri_t * cleri_keyword(uint32_t gid, const char * keyword, int ign_case) 24 | { 25 | size_t n = strlen(keyword); 26 | assert (n > 0 && n < UINT8_MAX); 27 | 28 | cleri_t * cl_object = cleri_new( 29 | gid, 30 | CLERI_TP_KEYWORD, 31 | &keyword__free, 32 | &keyword__parse); 33 | 34 | if (cl_object == NULL) 35 | { 36 | return NULL; 37 | } 38 | 39 | cl_object->via.keyword = cleri__malloc(cleri_keyword_t); 40 | 41 | if (cl_object->via.tokens == NULL) 42 | { 43 | free(cl_object); 44 | return NULL; 45 | } 46 | 47 | cl_object->via.keyword->keyword = keyword; 48 | cl_object->via.keyword->ign_case = ign_case; 49 | cl_object->via.keyword->len = n; 50 | 51 | return cl_object; 52 | } 53 | 54 | /* 55 | * Destroy keyword object. 56 | */ 57 | static void keyword__free(cleri_t * cl_object) 58 | { 59 | free(cl_object->via.keyword); 60 | } 61 | 62 | /* 63 | * Returns a node or NULL. In case or an error, pr->is_valid is set to -1. 64 | */ 65 | static cleri_node_t * keyword__parse( 66 | cleri_parse_t * pr, 67 | cleri_node_t * parent, 68 | cleri_t * cl_obj, 69 | cleri_rule_store_t * rule __attribute__((unused))) 70 | { 71 | size_t kw_len = cl_obj->via.keyword->len; 72 | cleri_node_t * node = NULL; 73 | const char * str = parent->str + parent->len; 74 | 75 | if ((strncmp(cl_obj->via.keyword->keyword, str, kw_len) == 0 || ( 76 | cl_obj->via.keyword->ign_case && 77 | strncasecmp(cl_obj->via.keyword->keyword, str, kw_len) == 0)) && 78 | cleri__kwcache_match(pr, str) == kw_len) 79 | { 80 | if ((node = cleri__node_new(cl_obj, str, kw_len)) != NULL) 81 | { 82 | parent->len += node->len; 83 | cleri__node_add(parent, node); 84 | } 85 | else 86 | { 87 | pr->is_valid = -1; /* error occurred */ 88 | } 89 | } 90 | else 91 | { 92 | /* Update expecting */ 93 | if (cleri__expecting_update(pr->expecting, cl_obj, str) == -1) 94 | { 95 | /* error occurred, node is already NULL */ 96 | pr->is_valid = -1; 97 | } 98 | } 99 | return node; 100 | } 101 | -------------------------------------------------------------------------------- /src/kwcache.c: -------------------------------------------------------------------------------- 1 | /* 2 | * kwcache.c - holds keyword regular expression result while parsing. 3 | */ 4 | #define PCRE2_CODE_UNIT_WIDTH 8 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define NOT_FOUND UINT8_MAX 13 | 14 | 15 | /* 16 | * Returns NULL in case an error has occurred. 17 | */ 18 | uint8_t * cleri__kwcache_new(const char * str) 19 | { 20 | size_t n = strlen(str); 21 | uint8_t * kwcache = cleri__mallocn(n, uint8_t); 22 | if (kwcache != NULL) 23 | { 24 | memset(kwcache, NOT_FOUND, n * sizeof(uint8_t)); 25 | } 26 | return kwcache; 27 | } 28 | 29 | /* 30 | * Returns 0 when no kw_match is found, -1 when an error has occurred, or the 31 | * new kwcache->len value. 32 | */ 33 | uint8_t cleri__kwcache_match(cleri_parse_t * pr, const char * str) 34 | { 35 | uint8_t * len; 36 | 37 | if (*str == '\0') 38 | { 39 | return 0; 40 | } 41 | 42 | len = &pr->kwcache[str - pr->str]; 43 | 44 | if (*len == NOT_FOUND) 45 | { 46 | int pcre_exec_ret; 47 | pcre_exec_ret = pcre2_match( 48 | pr->re_keywords, 49 | (PCRE2_SPTR8) str, 50 | PCRE2_ZERO_TERMINATED, 51 | 0, // start looking at this point 52 | 0, // OPTIONS 53 | pr->match_data, 54 | NULL); 55 | 56 | *len = pcre_exec_ret < 0 57 | ? 0 58 | : pcre2_get_ovector_pointer(pr->match_data)[1]; 59 | } 60 | return *len; 61 | } 62 | 63 | /* 64 | * Destroy kwcache. (parsing NULL is allowed) 65 | */ 66 | void cleri__kwcache_free(uint8_t * kwcache) 67 | { 68 | free(kwcache); 69 | } -------------------------------------------------------------------------------- /src/list.c: -------------------------------------------------------------------------------- 1 | /* 2 | * list.c - cleri list element. 3 | */ 4 | #include 5 | #include 6 | 7 | static void list__free(cleri_t * cl_object); 8 | static cleri_node_t * list__parse( 9 | cleri_parse_t * pr, 10 | cleri_node_t * parent, 11 | cleri_t * cl_obj, 12 | cleri_rule_store_t * rule); 13 | 14 | /* 15 | * Returns NULL in case an error has occurred. 16 | * 17 | * cl_obj : object to repeat 18 | * delimiter : object (Usually a Token) as delimiter 19 | * min : should be equal to or higher then 0. 20 | * max : should be equal to or higher then 0 but when 0 it 21 | * means unlimited. 22 | * opt_closing : when set to true (1) the list can be closed with a 23 | * delimiter. when false (0) this is not allowed. 24 | */ 25 | cleri_t * cleri_list( 26 | uint32_t gid, 27 | cleri_t * cl_obj, 28 | cleri_t * delimiter, 29 | size_t min, 30 | size_t max, 31 | int opt_closing) 32 | { 33 | if (cl_obj == NULL || delimiter == NULL) 34 | { 35 | return NULL; 36 | } 37 | 38 | cleri_t * cl_object = cleri_new( 39 | gid, 40 | CLERI_TP_LIST, 41 | &list__free, 42 | &list__parse); 43 | 44 | if (cl_object == NULL) 45 | { 46 | return NULL; 47 | } 48 | 49 | cl_object->via.list = cleri__malloc(cleri_list_t); 50 | 51 | if (cl_object->via.list == NULL) 52 | { 53 | free(cl_object); 54 | return NULL; 55 | } 56 | 57 | cl_object->via.list->cl_obj = cl_obj; 58 | cl_object->via.list->delimiter = delimiter; 59 | cl_object->via.list->min = min; 60 | cl_object->via.list->max = max; 61 | cl_object->via.list->opt_closing = opt_closing; 62 | 63 | cleri_incref(cl_obj); 64 | cleri_incref(delimiter); 65 | 66 | return cl_object; 67 | } 68 | 69 | /* 70 | * Destroy list object. 71 | */ 72 | static void list__free(cleri_t * cl_object) 73 | { 74 | cleri_free(cl_object->via.list->cl_obj); 75 | cleri_free(cl_object->via.list->delimiter); 76 | free(cl_object->via.list); 77 | } 78 | 79 | /* 80 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 81 | */ 82 | static cleri_node_t * list__parse( 83 | cleri_parse_t * pr, 84 | cleri_node_t * parent, 85 | cleri_t * cl_obj, 86 | cleri_rule_store_t * rule) 87 | { 88 | cleri_list_t * list = cl_obj->via.list; 89 | cleri_node_t * node; 90 | cleri_node_t * rnode; 91 | size_t i = 0; 92 | size_t j = 0; 93 | 94 | if ((node = cleri__node_new(cl_obj, parent->str + parent->len, 0)) == NULL) 95 | { 96 | pr->is_valid = -1; 97 | return NULL; 98 | } 99 | 100 | while (1) 101 | { 102 | rnode = cleri__parse_walk( 103 | pr, 104 | node, 105 | list->cl_obj, 106 | rule, 107 | i < list->min); // 1 = REQUIRED 108 | if (rnode == NULL || (++i == list->max && list->opt_closing == false)) 109 | { 110 | break; 111 | } 112 | rnode = cleri__parse_walk( 113 | pr, 114 | node, 115 | list->delimiter, 116 | rule, 117 | i < list->min); // 1 = REQUIRED 118 | if (rnode == NULL || ++j == list->max) 119 | { 120 | break; 121 | } 122 | } 123 | if (i < list->min || (list->opt_closing == false && i && i == j)) 124 | { 125 | cleri__node_free(node); 126 | return NULL; 127 | } 128 | parent->len += node->len; 129 | cleri__node_add(parent, node); 130 | return node; 131 | } 132 | -------------------------------------------------------------------------------- /src/node.c: -------------------------------------------------------------------------------- 1 | /* 2 | * node.c - node is created while parsing a string. a node old the result 3 | * for one element. 4 | */ 5 | #include 6 | #include 7 | 8 | static cleri_node_t CLERI__EMPTY_NODE = { 9 | .children=NULL, 10 | .next=NULL, 11 | .cl_obj=NULL, 12 | .len=0, 13 | .str=NULL, 14 | .ref=1, 15 | }; 16 | 17 | cleri_node_t * CLERI_EMPTY_NODE = &CLERI__EMPTY_NODE; 18 | 19 | /* 20 | * Returns NULL in case an error has occurred. 21 | */ 22 | cleri_node_t * cleri__node_new(cleri_t * cl_obj, const char * str, size_t len) 23 | { 24 | cleri_node_t * node = cleri__malloc(cleri_node_t); 25 | 26 | if (node != NULL) 27 | { 28 | node->cl_obj = cl_obj; 29 | node->ref = 1; 30 | 31 | node->str = str; 32 | node->len = len; 33 | node->children = NULL; 34 | node->next = NULL; 35 | node->data = &node->children; 36 | } 37 | return node; 38 | } 39 | 40 | cleri_node_t * cleri__node_dup(cleri_node_t * node) 41 | { 42 | cleri_node_t * dup = cleri__malloc(cleri_node_t); 43 | if (dup != NULL) 44 | { 45 | dup->cl_obj = node->cl_obj; 46 | dup->ref = 1; 47 | 48 | dup->str = node->str; 49 | dup->len = node->len; 50 | 51 | node = node->children; 52 | 53 | dup->children = node; 54 | 55 | while(node) 56 | { 57 | node->ref++; 58 | node = node->next; 59 | } 60 | 61 | dup->next = NULL; 62 | } 63 | return dup; 64 | } 65 | 66 | void cleri__node_free(cleri_node_t * node) 67 | { 68 | cleri_node_t * next; 69 | 70 | /* node can be NULL or this could be an CLERI_EMPTY_NODE */ 71 | if (node == NULL || --node->ref) 72 | { 73 | return; 74 | } 75 | 76 | next = node->children; 77 | 78 | while (next != NULL) 79 | { 80 | cleri_node_t * tmp = next->next; 81 | next->next = NULL; 82 | cleri__node_free(next); 83 | next = tmp; 84 | } 85 | 86 | free(node); 87 | } 88 | -------------------------------------------------------------------------------- /src/olist.c: -------------------------------------------------------------------------------- 1 | /* 2 | * olist.c - linked list for keeping cleri objects. 3 | */ 4 | #include 5 | #include 6 | 7 | /* 8 | * Returns NULL in case an error has occurred. 9 | */ 10 | cleri_olist_t * cleri__olist_new(void) 11 | { 12 | cleri_olist_t * olist = cleri__malloc(cleri_olist_t); 13 | if (olist != NULL) 14 | { 15 | olist->cl_obj = NULL; 16 | olist->next = NULL; 17 | } 18 | return olist; 19 | } 20 | 21 | /* 22 | * Returns 0 if successful or -1 in case of an error. 23 | * The list remains unchanged in case of an error and the object reference 24 | * counter will be incremented when successful. 25 | */ 26 | int cleri__olist_append(cleri_olist_t * olist, cleri_t * cl_object) 27 | { 28 | if (cl_object == NULL) 29 | { 30 | return -1; 31 | } 32 | 33 | if (olist->cl_obj == NULL) 34 | { 35 | cleri_incref(cl_object); 36 | olist->cl_obj = cl_object; 37 | olist->next = NULL; 38 | return 0; 39 | } 40 | 41 | while (olist->next != NULL) 42 | { 43 | olist = olist->next; 44 | } 45 | 46 | olist->next = cleri__malloc(cleri_olist_t); 47 | 48 | if (olist->next == NULL) 49 | { 50 | return -1; 51 | } 52 | 53 | cleri_incref(cl_object); 54 | olist->next->cl_obj = cl_object; 55 | olist->next->next = NULL; 56 | 57 | return 0; 58 | } 59 | 60 | /* 61 | * Exactly the same as cleri__olist_append, except the reference counter 62 | * will not be incremented. 63 | */ 64 | int cleri__olist_append_nref(cleri_olist_t * olist, cleri_t * cl_object) 65 | { 66 | if (cl_object == NULL) 67 | { 68 | return -1; 69 | } 70 | 71 | if (olist->cl_obj == NULL) 72 | { 73 | olist->cl_obj = cl_object; 74 | olist->next = NULL; 75 | return 0; 76 | } 77 | 78 | while (olist->next != NULL) 79 | { 80 | olist = olist->next; 81 | } 82 | 83 | olist->next = cleri__malloc(cleri_olist_t); 84 | 85 | if (olist->next == NULL) 86 | { 87 | return -1; 88 | } 89 | 90 | olist->next->cl_obj = cl_object; 91 | olist->next->next = NULL; 92 | 93 | return 0; 94 | } 95 | 96 | /* 97 | * Destroy the olist and decrement the reference counter for each object in 98 | * the list. (NULL is allowed as olist and does nothing) 99 | */ 100 | void cleri__olist_free(cleri_olist_t * olist) 101 | { 102 | cleri_olist_t * next; 103 | while (olist != NULL) 104 | { 105 | next = olist->next; 106 | cleri_free(olist->cl_obj); 107 | free(olist); 108 | olist = next; 109 | } 110 | } 111 | 112 | 113 | /* 114 | * Empty the object list but do not free the list. This will not decrement 115 | * the reference counters for object in the list. 116 | */ 117 | void cleri__olist_empty(cleri_olist_t * olist) 118 | { 119 | cleri_olist_t * current; 120 | 121 | if (olist == NULL) 122 | { 123 | return; 124 | } 125 | 126 | /* set root object to NULL but we do not need to free root */ 127 | olist->cl_obj = NULL; 128 | 129 | /* set root next to NULL */ 130 | current = olist->next; 131 | olist->next = NULL; 132 | 133 | /* cleanup all the rest */ 134 | while (current != NULL) 135 | { 136 | olist = current->next; 137 | free(current); 138 | current = olist; 139 | } 140 | } 141 | 142 | /* 143 | * Cancel building olist. Used to recover from an error while creating 144 | * a new element. 145 | */ 146 | void cleri__olist_cancel(cleri_olist_t * olist) 147 | { 148 | cleri_olist_t * current = olist->next; 149 | 150 | /* cleanup all the rest */ 151 | while (current != NULL) 152 | { 153 | olist->cl_obj->ref--; 154 | current = current->next; 155 | } 156 | cleri__olist_empty(olist); 157 | } 158 | 159 | void cleri__olist_unique(cleri_olist_t * olist) 160 | { 161 | while (olist != NULL && olist->next != NULL) 162 | { 163 | cleri_olist_t * test = olist; 164 | while (test->next != NULL) 165 | { 166 | if (olist->cl_obj == test->next->cl_obj) 167 | { 168 | cleri_olist_t * tmp = test->next->next; 169 | free(test->next); 170 | test->next = tmp; 171 | continue; 172 | } 173 | test = test->next; 174 | } 175 | olist = olist->next; 176 | } 177 | } -------------------------------------------------------------------------------- /src/optional.c: -------------------------------------------------------------------------------- 1 | /* 2 | * optional.c - cleri optional element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | static void optional__free(cleri_t * cl_object); 9 | 10 | static cleri_node_t * optional__parse( 11 | cleri_parse_t * pr, 12 | cleri_node_t * parent, 13 | cleri_t * cl_obj, 14 | cleri_rule_store_t * rule); 15 | 16 | /* 17 | * Returns NULL and in case an error has occurred. 18 | */ 19 | cleri_t * cleri_optional(uint32_t gid, cleri_t * cl_obj) 20 | { 21 | if (cl_obj == NULL) 22 | { 23 | return NULL; 24 | } 25 | 26 | cleri_t * cl_object = cleri_new( 27 | gid, 28 | CLERI_TP_OPTIONAL, 29 | &optional__free, 30 | &optional__parse); 31 | 32 | if (cl_object == NULL) 33 | { 34 | return NULL; 35 | } 36 | 37 | cl_object->via.optional = cleri__malloc(cleri_optional_t); 38 | 39 | if (cl_object->via.optional == NULL) 40 | { 41 | free(cl_object); 42 | return NULL; 43 | } 44 | 45 | cl_object->via.optional->cl_obj = cl_obj; 46 | 47 | cleri_incref(cl_obj); 48 | 49 | return cl_object; 50 | } 51 | 52 | /* 53 | * Destroy optional object. 54 | */ 55 | static void optional__free(cleri_t * cl_object) 56 | { 57 | cleri_free(cl_object->via.optional->cl_obj); 58 | free(cl_object->via.optional); 59 | } 60 | 61 | /* 62 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 63 | */ 64 | static cleri_node_t * optional__parse( 65 | cleri_parse_t * pr, 66 | cleri_node_t * parent, 67 | cleri_t * cl_obj, 68 | cleri_rule_store_t * rule) 69 | { 70 | cleri_node_t * node; 71 | cleri_node_t * rnode; 72 | 73 | if ((pr->flags & CLERI_FLAG_EXCLUDE_OPTIONAL) && !cl_obj->gid) 74 | { 75 | node = cleri__parse_walk( 76 | pr, 77 | parent, 78 | cl_obj->via.optional->cl_obj, 79 | rule, 80 | CLERI__EXP_MODE_OPTIONAL); 81 | return node ? node : CLERI_EMPTY_NODE; 82 | } 83 | 84 | if ((node = cleri__node_new(cl_obj, parent->str + parent->len, 0)) == NULL) 85 | { 86 | pr->is_valid = -1; 87 | return NULL; 88 | } 89 | 90 | rnode = cleri__parse_walk( 91 | pr, 92 | node, 93 | cl_obj->via.optional->cl_obj, 94 | rule, 95 | CLERI__EXP_MODE_OPTIONAL); 96 | 97 | if (rnode != NULL) 98 | { 99 | parent->len += node->len; 100 | cleri__node_add(parent, node); 101 | return node; 102 | } 103 | 104 | cleri__node_free(node); 105 | return CLERI_EMPTY_NODE; 106 | } 107 | -------------------------------------------------------------------------------- /src/parse.c: -------------------------------------------------------------------------------- 1 | /* 2 | * parse.c - this contains everything for parsing a string to a grammar. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* 12 | * Return a parse result. In case of a memory allocation error the return value 13 | * will be NULL. 14 | */ 15 | cleri_parse_t * cleri_parse2( 16 | cleri_grammar_t * grammar, 17 | const char * str, 18 | int flags) 19 | { 20 | cleri_node_t * nd; 21 | cleri_parse_t * pr; 22 | const char * end; 23 | const char * test; 24 | 25 | /* prepare parsing */ 26 | pr = cleri__malloc(cleri_parse_t); 27 | if (pr == NULL) 28 | { 29 | return NULL; 30 | } 31 | 32 | pr->flags = flags; 33 | pr->str = str; 34 | pr->tree = NULL; 35 | pr->kwcache = NULL; 36 | pr->expecting = NULL; 37 | pr->is_valid = 0; 38 | 39 | if ( (pr->tree = cleri__node_new(NULL, str, 0)) == NULL || 40 | (pr->kwcache = cleri__kwcache_new(str)) == NULL || 41 | (pr->expecting = cleri__expecting_new(str, flags)) == NULL) 42 | { 43 | cleri_parse_free(pr); 44 | return NULL; 45 | } 46 | 47 | pr->re_keywords = grammar->re_keywords; 48 | pr->match_data = grammar->match_data; 49 | 50 | /* do the actual parsing */ 51 | nd = cleri__parse_walk( 52 | pr, 53 | pr->tree, 54 | grammar->start, 55 | NULL, 56 | CLERI__EXP_MODE_REQUIRED); 57 | 58 | /* When is_valid is -1, an allocation error has occurred. */ 59 | if (pr->is_valid == -1) 60 | { 61 | cleri_parse_free(pr); 62 | return NULL; 63 | } 64 | 65 | pr->is_valid = nd != NULL; 66 | 67 | /* process the parse result */ 68 | end = pr->tree->str + pr->tree->len; 69 | 70 | /* check if we are at the end of the string */ 71 | if (pr->is_valid) for (test = end; *test; test++) 72 | { 73 | if (!isspace(*test)) 74 | { 75 | pr->is_valid = false; 76 | if (pr->expecting->required) 77 | { 78 | if (cleri__expecting_set_mode( 79 | pr->expecting, 80 | end, 81 | CLERI__EXP_MODE_REQUIRED) == -1 || 82 | cleri__expecting_update( 83 | pr->expecting, 84 | CLERI_END_OF_STATEMENT, 85 | end) == -1) 86 | { 87 | cleri_parse_free(pr); 88 | return NULL; 89 | } 90 | cleri__expecting_combine(pr->expecting); 91 | } 92 | else if (cleri__expecting_update( 93 | pr->expecting, 94 | CLERI_END_OF_STATEMENT, 95 | end)) 96 | { 97 | cleri_parse_free(pr); 98 | return NULL; 99 | } 100 | break; 101 | } 102 | } 103 | 104 | pr->pos = pr->is_valid 105 | ? pr->tree->len 106 | : (size_t) (pr->expecting->str - pr->str); 107 | 108 | cleri__olist_unique(pr->expecting->required); 109 | pr->expect = pr->expecting->required; 110 | 111 | return pr; 112 | } 113 | 114 | /* 115 | * Destroy parser. (parsing NULL is allowed) 116 | */ 117 | void cleri_parse_free(cleri_parse_t * pr) 118 | { 119 | cleri__node_free(pr->tree); 120 | free(pr->kwcache); 121 | if (pr->expecting != NULL) 122 | { 123 | cleri__expecting_free(pr->expecting); 124 | } 125 | free(pr); 126 | } 127 | 128 | /* 129 | * Reset expect to start 130 | */ 131 | void cleri_parse_expect_start(cleri_parse_t * pr) 132 | { 133 | pr->expect = pr->expecting->required; 134 | } 135 | 136 | static void parse__line_pos(cleri_parse_t * pr, size_t * line, size_t * pos) 137 | { 138 | size_t n = pr->pos; 139 | const char * pt = pr->str; 140 | *pos = 0; 141 | *line = 1; 142 | while (n--) 143 | { 144 | if (*pt == '\n') 145 | { 146 | if (!n) 147 | break; 148 | 149 | ++pt; 150 | if (*pt == '\r') 151 | { 152 | if (!--n) 153 | break; 154 | ++pt; 155 | } 156 | 157 | ++(*line); 158 | *pos = 0; 159 | continue; 160 | } 161 | if (*pt == '\r') 162 | { 163 | if (!n) 164 | break; 165 | 166 | ++pt; 167 | if (*(++pt) == '\n' ) 168 | { 169 | if (!--n) 170 | break; 171 | 172 | ++pt; 173 | ++(*line); 174 | *pos = 0; 175 | } 176 | 177 | ++(*line); 178 | *pos = 0; 179 | continue; 180 | } 181 | ++(*pos); 182 | ++pt; 183 | } 184 | } 185 | 186 | /* 187 | * Print parse result to a string. The return value is equal to the snprintf 188 | * function. Argument `translate_cb` maybe NULL or a function which may return 189 | * a string based on the `cleri_t`. This allows for returning nice strings for 190 | * regular expressions. The function may return NULL if it has no translation 191 | * for the given regular expression. 192 | */ 193 | int cleri_parse_strn( 194 | char * s, 195 | size_t n, 196 | cleri_parse_t * pr, 197 | cleri_translate_t * translate) 198 | { 199 | int rc, count = 0; 200 | size_t i, m, line, pos; 201 | cleri_t * o; 202 | const char * expect; 203 | const char * template; 204 | if (pr == NULL) 205 | { 206 | return snprintf(s, n, 207 | "no parse result, a possible reason might be that the maximum " 208 | "recursion depth of %d has been reached", MAX_RECURSION_DEPTH); 209 | } 210 | if (pr->is_valid) 211 | { 212 | return snprintf(s, n, "parsed successfully"); 213 | } 214 | /* make sure expecting is at start */ 215 | cleri_parse_expect_start(pr); 216 | 217 | parse__line_pos(pr, &line, &pos); 218 | 219 | rc = snprintf(s, n, "error at line %zu, position %zu", line, pos); 220 | if (rc < 0) 221 | { 222 | return rc; 223 | } 224 | i = rc; 225 | 226 | expect = pr->str + pr->pos; 227 | if (isgraph(*expect)) 228 | { 229 | size_t nc = cleri__kwcache_match(pr, expect); 230 | const char * pt = expect; 231 | const unsigned int max_chars = 20; 232 | 233 | if (nc < 1) 234 | { 235 | while (isdigit(*pt)) 236 | ++pt; 237 | nc = pt - expect; 238 | } 239 | 240 | m = (i < n) ? n-i : 0; 241 | 242 | if (nc > 1) 243 | { 244 | rc = nc > max_chars 245 | ? snprintf(s+i, m, ", unexpected `%.*s...`", max_chars, expect) 246 | : snprintf(s+i, m, ", unexpected `%.*s`", (int) nc, expect); 247 | } 248 | else 249 | { 250 | rc = snprintf(s+i, m, ", unexpected character `%c`", *expect); 251 | } 252 | 253 | if (rc < 0) 254 | { 255 | return rc; 256 | } 257 | 258 | i += rc; 259 | } 260 | 261 | 262 | while (pr->expect) 263 | { 264 | o = pr->expect->cl_obj; 265 | if (!translate || !(expect = (*translate)(o))) switch(o->tp) 266 | { 267 | case CLERI_TP_END_OF_STATEMENT: 268 | expect = "end_of_statement"; 269 | break; 270 | case CLERI_TP_KEYWORD: 271 | expect = o->via.keyword->keyword; 272 | break; 273 | case CLERI_TP_TOKENS: 274 | expect = o->via.tokens->spaced; 275 | break; 276 | case CLERI_TP_TOKEN: 277 | expect = o->via.token->token; 278 | break; 279 | default: 280 | expect = ""; /* continue */ 281 | } 282 | 283 | if (*expect == '\0') 284 | { 285 | /* ignore empty strings */ 286 | pr->expect = pr->expect->next; 287 | continue; 288 | } 289 | 290 | /* make sure len is not greater than the maximum size */ 291 | m = (i < n) ? n-i : 0; 292 | 293 | /* we use count = 0 to print the first one, then for the others 294 | * a comma prefix and the last with -or- 295 | * 296 | * TODO: could be improved since the last `or` might never be added 297 | */ 298 | template = !count++ 299 | ? ", expecting: %s" 300 | : pr->expect->next 301 | ? ", %s" 302 | : " or %s"; 303 | rc = snprintf(s+i, m, template, expect); 304 | if (rc < 0) 305 | { 306 | return rc; 307 | } 308 | i += rc; 309 | 310 | pr->expect = pr->expect->next; 311 | } 312 | return (int) i; 313 | } 314 | 315 | /* 316 | * Walk a parser object. 317 | * (recursive function, called from each parse_object function) 318 | * Returns a node or NULL. (In case of error one should check pr->is_valid) 319 | */ 320 | cleri_node_t * cleri__parse_walk( 321 | cleri_parse_t * pr, 322 | cleri_node_t * parent, 323 | cleri_t * cl_obj, 324 | cleri_rule_store_t * rule, 325 | int mode) 326 | { 327 | const char * str = parent->str + parent->len; 328 | 329 | /* set parent len to next none white space char */ 330 | for (; isspace(*str); ++str, ++parent->len); 331 | 332 | /* set expecting mode */ 333 | if (cleri__expecting_set_mode(pr->expecting, parent->str, mode) == -1) 334 | { 335 | pr->is_valid = -1; 336 | return NULL; 337 | } 338 | 339 | /* note that the actual node is returned or NULL but we do not 340 | * actually need the node. (boolean true/false would be enough) 341 | */ 342 | return (*cl_obj->parse_object)(pr, parent, cl_obj, rule); 343 | } 344 | -------------------------------------------------------------------------------- /src/prio.c: -------------------------------------------------------------------------------- 1 | /* 2 | * prio.c - cleri prio element. (this element create a cleri rule object 3 | * holding this prio element) 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static void prio__free(cleri_t * cl_obj); 13 | 14 | static cleri_node_t * prio__parse( 15 | cleri_parse_t * pr, 16 | cleri_node_t * parent, 17 | cleri_t * cl_obj, 18 | cleri_rule_store_t * rule); 19 | 20 | /* 21 | * Returns NULL in case an error has occurred. 22 | */ 23 | cleri_t * cleri_prio(uint32_t gid, size_t len, ...) 24 | { 25 | va_list ap; 26 | cleri_t * cl_object = cleri_new( 27 | 0, 28 | CLERI_TP_PRIO, 29 | &prio__free, 30 | &prio__parse); 31 | 32 | if (cl_object == NULL) 33 | { 34 | return NULL; 35 | } 36 | 37 | cl_object->via.prio = cleri__malloc(cleri_prio_t); 38 | 39 | if (cl_object->via.prio == NULL) 40 | { 41 | free(cl_object); 42 | return NULL; 43 | } 44 | 45 | cl_object->via.prio->olist = cleri__olist_new(); 46 | 47 | if (cl_object->via.prio->olist == NULL) 48 | { 49 | cleri_free(cl_object); 50 | return NULL; 51 | } 52 | 53 | va_start(ap, len); 54 | while(len--) 55 | { 56 | if (cleri__olist_append( 57 | cl_object->via.prio->olist, 58 | va_arg(ap, cleri_t *))) 59 | { 60 | cleri__olist_cancel(cl_object->via.prio->olist); 61 | cleri_free(cl_object); 62 | cl_object = NULL; 63 | } 64 | } 65 | va_end(ap); 66 | 67 | return cleri__rule(gid, cl_object); 68 | } 69 | 70 | /* 71 | * Destroy prio object. 72 | */ 73 | static void prio__free(cleri_t * cl_object) 74 | { 75 | cleri__olist_free(cl_object->via.prio->olist); 76 | free(cl_object->via.prio); 77 | } 78 | 79 | /* 80 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 81 | */ 82 | static cleri_node_t * prio__parse( 83 | cleri_parse_t * pr, 84 | cleri_node_t * parent, 85 | cleri_t * cl_obj, 86 | cleri_rule_store_t * rule) 87 | { 88 | cleri_olist_t * olist; 89 | cleri_node_t * node; 90 | cleri_node_t * rnode; 91 | cleri_rule_tested_t * tested; 92 | const char * str = parent->str + parent->len; 93 | 94 | /* initialize and return rule test, or return an existing test 95 | * if *str is already in tested */ 96 | if ( rule->depth++ > MAX_RECURSION_DEPTH || 97 | cleri__rule_init(&tested, &rule->tested, str) == CLERI_RULE_ERROR) 98 | { 99 | pr->is_valid = -1; 100 | return NULL; 101 | } 102 | 103 | olist = cl_obj->via.prio->olist; 104 | 105 | while (olist != NULL) 106 | { 107 | if ((node = cleri__node_new(cl_obj, str, 0)) == NULL) 108 | { 109 | pr->is_valid = -1; 110 | return NULL; 111 | } 112 | rnode = cleri__parse_walk( 113 | pr, 114 | node, 115 | olist->cl_obj, 116 | rule, 117 | CLERI__EXP_MODE_REQUIRED); 118 | 119 | if (rnode != NULL && 120 | (tested->node == NULL || node->len > tested->node->len)) 121 | { 122 | if (tested->node != NULL) 123 | { 124 | /* 125 | * It is required to decrement an extra reference here, one 126 | * belongs to the parse result, and one for the tested rule. 127 | * The node->ref increment below is required for when a str 128 | * position is visited a second time by another parent. 129 | */ 130 | --tested->node->ref; 131 | cleri__node_free(tested->node); 132 | } 133 | tested->node = node; 134 | node->ref++; 135 | } 136 | else 137 | { 138 | cleri__node_free(node); 139 | } 140 | olist = olist->next; 141 | } 142 | 143 | rule->depth--; 144 | 145 | if (tested->node != NULL) 146 | { 147 | parent->len += tested->node->len; 148 | cleri__node_add(parent, tested->node); 149 | return tested->node; 150 | } 151 | return NULL; 152 | } 153 | 154 | -------------------------------------------------------------------------------- /src/ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ref.c - cleri ref element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | static void ref__free(cleri_t * cl_object); 9 | 10 | /* 11 | * Returns NULL in case an error has occurred. 12 | */ 13 | cleri_t * cleri_ref(void) 14 | { 15 | cleri_t * cl_object = cleri_new( 16 | 0, 17 | CLERI_TP_REF, 18 | &ref__free, 19 | NULL); 20 | 21 | return cl_object; 22 | } 23 | 24 | /* 25 | * Both ref and cl_obj are not allowed to be NULL. 26 | */ 27 | void cleri_ref_set(cleri_t * ref, cleri_t * cl_obj) 28 | { 29 | /* cl_obj should have no other refs */ 30 | assert (ref != NULL && 31 | ref->tp == CLERI_TP_REF && 32 | cl_obj != NULL && 33 | cl_obj->ref == 1); 34 | 35 | /* assign properties, except ref counter */ 36 | ref->gid = cl_obj->gid; 37 | ref->free_object = cl_obj->free_object; 38 | ref->parse_object = cl_obj->parse_object; 39 | ref->tp = cl_obj->tp; 40 | ref->via = cl_obj->via; 41 | 42 | /* free *cl_obj and set the pointer to the ref object */ 43 | free(cl_obj); 44 | } 45 | 46 | 47 | /* 48 | * Destroy ref object. (only used when ref is not set) 49 | */ 50 | static void ref__free(cleri_t * cl_object __attribute__((unused))) 51 | { 52 | /* nothing todo */ 53 | } 54 | -------------------------------------------------------------------------------- /src/regex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * regex.c - cleri regular expression element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void regex__free(cleri_t * cl_object); 11 | 12 | static cleri_node_t * regex__parse( 13 | cleri_parse_t * pr, 14 | cleri_node_t * parent, 15 | cleri_t * cl_obj, 16 | cleri_rule_store_t * rule); 17 | 18 | /* 19 | * Returns a regex object or NULL in case of an error. 20 | * 21 | * Argument pattern must start with character '^'. Be sure to check the pattern 22 | * for the '^' character before calling this function. 23 | * 24 | * Warning: this function could write to stderr in case the pattern could not 25 | * be compiled. 26 | */ 27 | cleri_t * cleri_regex(uint32_t gid, const char * pattern) 28 | { 29 | cleri_t * cl_object; 30 | int pcre_error_num; 31 | PCRE2_SIZE pcre_error_offset; 32 | 33 | /* 34 | * starting with ^ is not required as flags may go first 35 | * assert (pattern[0] == '^'); 36 | */ 37 | 38 | cl_object = cleri_new( 39 | gid, 40 | CLERI_TP_REGEX, 41 | ®ex__free, 42 | ®ex__parse); 43 | 44 | if (cl_object == NULL) 45 | { 46 | return NULL; 47 | } 48 | 49 | cl_object->via.regex = cleri__malloc(cleri_regex_t); 50 | 51 | if (cl_object->via.regex == NULL) 52 | { 53 | free(cl_object); 54 | return NULL; 55 | } 56 | 57 | cl_object->via.regex->regex = pcre2_compile( 58 | (PCRE2_SPTR8) pattern, 59 | PCRE2_ZERO_TERMINATED, 60 | 0, 61 | &pcre_error_num, 62 | &pcre_error_offset, 63 | NULL); 64 | 65 | if(cl_object->via.regex->regex == NULL) 66 | { 67 | PCRE2_UCHAR buffer[256]; 68 | pcre2_get_error_message(pcre_error_num, buffer, sizeof(buffer)); 69 | 70 | fprintf(stderr, 71 | "error: cannot compile '%s' (%s)\n", 72 | pattern, 73 | buffer); 74 | free(cl_object->via.regex); 75 | free(cl_object); 76 | return NULL; 77 | } 78 | 79 | cl_object->via.regex->match_data = pcre2_match_data_create_from_pattern( 80 | cl_object->via.regex->regex, 81 | NULL); 82 | 83 | if (cl_object->via.regex->match_data == NULL) 84 | { 85 | pcre2_code_free(cl_object->via.regex->regex); 86 | fprintf(stderr, "error: cannot create match data\n"); 87 | free(cl_object->via.regex); 88 | free(cl_object); 89 | return NULL; 90 | return NULL; 91 | } 92 | 93 | return cl_object; 94 | } 95 | 96 | /* 97 | * Destroy regex object. 98 | */ 99 | static void regex__free(cleri_t * cl_object) 100 | { 101 | pcre2_match_data_free(cl_object->via.regex->match_data); 102 | pcre2_code_free(cl_object->via.regex->regex); 103 | free(cl_object->via.regex); 104 | } 105 | 106 | /* 107 | * Returns a node or NULL. In case of an error, pr->is_valid is set to -1 108 | */ 109 | static cleri_node_t * regex__parse( 110 | cleri_parse_t * pr, 111 | cleri_node_t * parent, 112 | cleri_t * cl_obj, 113 | cleri_rule_store_t * rule __attribute__((unused))) 114 | { 115 | int pcre_exec_ret; 116 | PCRE2_SIZE * ovector; 117 | const char * str = parent->str + parent->len; 118 | cleri_node_t * node; 119 | 120 | pcre_exec_ret = pcre2_match( 121 | cl_obj->via.regex->regex, 122 | (PCRE2_SPTR8) str, 123 | PCRE2_ZERO_TERMINATED, 124 | 0, // start looking at this point 125 | 0, // OPTIONS 126 | cl_obj->via.regex->match_data, 127 | NULL); 128 | 129 | if (pcre_exec_ret < 0) 130 | { 131 | if (cleri__expecting_update(pr->expecting, cl_obj, str) == -1) 132 | { 133 | pr->is_valid = -1; /* error occurred */ 134 | } 135 | return NULL; 136 | } 137 | ovector = pcre2_get_ovector_pointer(cl_obj->via.regex->match_data); 138 | 139 | /* since each regex pattern should start with ^ we now sub_str_vec[0] 140 | * should be 0. sub_str_vec[1] contains the end position in the sting 141 | */ 142 | if ((node = cleri__node_new(cl_obj, str, (size_t) ovector[1])) != NULL) 143 | { 144 | parent->len += node->len; 145 | cleri__node_add(parent, node); 146 | } 147 | else 148 | { 149 | pr->is_valid = -1; /* error occurred */ 150 | } 151 | 152 | return node; 153 | } 154 | -------------------------------------------------------------------------------- /src/repeat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * repeat.c - cleri regular repeat element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | 8 | static void repeat__free(cleri_t * cl_object); 9 | 10 | static cleri_node_t * repeat__parse( 11 | cleri_parse_t * pr, 12 | cleri_node_t * parent, 13 | cleri_t * cl_obj, 14 | cleri_rule_store_t * rule); 15 | 16 | /* 17 | * Returns NULL in case an error has occurred. 18 | * 19 | * cl_ob : object to repeat 20 | * min : should be equal to or higher then 0. 21 | * max : should be equal to or higher then min, or 0 which means 22 | * unlimited. 23 | */ 24 | cleri_t * cleri_repeat(uint32_t gid, cleri_t * cl_obj, size_t min, size_t max) 25 | { 26 | if (cl_obj == NULL) 27 | { 28 | return NULL; 29 | } 30 | 31 | assert (!max || max >= min); 32 | 33 | cleri_t * cl_object = cleri_new( 34 | gid, 35 | CLERI_TP_REPEAT, 36 | &repeat__free, 37 | &repeat__parse); 38 | 39 | if (cl_object == NULL) 40 | { 41 | return NULL; 42 | } 43 | 44 | cl_object->via.repeat = cleri__malloc(cleri_repeat_t); 45 | 46 | if (cl_object->via.repeat == NULL) 47 | { 48 | free(cl_object); 49 | return NULL; 50 | } 51 | 52 | cl_object->via.repeat->cl_obj = cl_obj; 53 | cl_object->via.repeat->min = min; 54 | cl_object->via.repeat->max = max; 55 | 56 | cleri_incref(cl_obj); 57 | 58 | return cl_object; 59 | } 60 | 61 | /* 62 | * Destroy repeat object. 63 | */ 64 | static void repeat__free(cleri_t * cl_object) 65 | { 66 | cleri_free(cl_object->via.repeat->cl_obj); 67 | free(cl_object->via.repeat); 68 | } 69 | 70 | /* 71 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 72 | */ 73 | static cleri_node_t * repeat__parse( 74 | cleri_parse_t * pr, 75 | cleri_node_t * parent, 76 | cleri_t * cl_obj, 77 | cleri_rule_store_t * rule) 78 | { 79 | cleri_node_t * node; 80 | cleri_node_t * rnode; 81 | size_t i; 82 | if ((node = cleri__node_new(cl_obj, parent->str + parent->len, 0)) == NULL) 83 | { 84 | pr->is_valid = -1; 85 | return NULL; 86 | } 87 | 88 | for (i = 0; 89 | cl_obj->via.repeat->max == 0 || i < cl_obj->via.repeat->max; 90 | i++) 91 | { 92 | rnode = cleri__parse_walk( 93 | pr, 94 | node, 95 | cl_obj->via.repeat->cl_obj, 96 | rule, 97 | i < cl_obj->via.repeat->min); // 1 = REQUIRED 98 | if (rnode == NULL) 99 | { 100 | break; 101 | } 102 | } 103 | 104 | if (i < cl_obj->via.repeat->min) 105 | { 106 | cleri__node_free(node); 107 | return NULL; 108 | } 109 | parent->len += node->len; 110 | cleri__node_add(parent, node); 111 | return node; 112 | } 113 | -------------------------------------------------------------------------------- /src/rule.c: -------------------------------------------------------------------------------- 1 | /* 2 | * rule.c - cleri regular rule element. (do not directly use this element but 3 | * create a 'prio' instead which will be wrapped by a rule element) 4 | */ 5 | #include 6 | #include 7 | 8 | static void rule__free(cleri_t * cl_object); 9 | static cleri_node_t * rule__parse( 10 | cleri_parse_t * pr, 11 | cleri_node_t * parent, 12 | cleri_t * cl_obj, 13 | cleri_rule_store_t * rule); 14 | static void rule__tested_free(cleri_rule_tested_t * tested); 15 | 16 | /* 17 | * Returns NULL in case an error has occurred. 18 | */ 19 | cleri_t * cleri__rule(uint32_t gid, cleri_t * cl_obj) 20 | { 21 | if (cl_obj == NULL) 22 | { 23 | return NULL; 24 | } 25 | 26 | cleri_t * cl_object = cleri_new( 27 | gid, 28 | CLERI_TP_RULE, 29 | &rule__free, 30 | &rule__parse); 31 | 32 | if (cl_object != NULL) 33 | { 34 | cl_object->via.rule = cleri__malloc(cleri_rule_t); 35 | 36 | if (cl_object->via.rule == NULL) 37 | { 38 | free(cl_object); 39 | cl_object = NULL; 40 | } 41 | else 42 | { 43 | cl_object->via.rule->cl_obj = cl_obj; 44 | cleri_incref(cl_obj); 45 | } 46 | } 47 | 48 | return cl_object; 49 | } 50 | 51 | /* 52 | * Initialize a rule and return the test result. 53 | * Result can be either CLERI_RULE_TRUE, CLERI_RULE_FALSE or CLERI_RULE_ERROR. 54 | * 55 | * - CLERI_RULE_TRUE: a new test is created 56 | * - CLERI_RULE_FALSE: no new test is created 57 | * - CLERI_RULE_ERROR: an error occurred 58 | */ 59 | cleri_rule_test_t cleri__rule_init( 60 | cleri_rule_tested_t ** target, 61 | cleri_rule_tested_t * tested, 62 | const char * str) 63 | { 64 | /* 65 | * return true (1) when a new test is created, false (0) when not. 66 | */ 67 | (*target) = tested; 68 | 69 | if ((*target)->str == NULL) 70 | { 71 | (*target)->str = str; 72 | return CLERI_RULE_TRUE; 73 | } 74 | 75 | if ((*target)->str == str) 76 | { 77 | return CLERI_RULE_FALSE; 78 | } 79 | 80 | while (((*target) = (*target)->next) != NULL && str <= (*target)->str) 81 | { 82 | if ((*target)->str == str) 83 | { 84 | return CLERI_RULE_FALSE; 85 | } 86 | tested = (*target); 87 | } 88 | 89 | *target = cleri__malloc(cleri_rule_tested_t); 90 | 91 | if (*target == NULL) 92 | { 93 | return CLERI_RULE_ERROR; 94 | } 95 | 96 | (*target)->str = str; 97 | (*target)->node = NULL; 98 | (*target)->next = tested->next; 99 | tested->next = *target; 100 | return CLERI_RULE_TRUE; 101 | } 102 | 103 | static void rule__free(cleri_t * cl_object) 104 | { 105 | cleri_free(cl_object->via.rule->cl_obj); 106 | free(cl_object->via.rule); 107 | } 108 | 109 | /* 110 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 111 | */ 112 | static cleri_node_t * rule__parse( 113 | cleri_parse_t * pr, 114 | cleri_node_t * parent, 115 | cleri_t * cl_obj, 116 | cleri_rule_store_t * __rule __attribute__((unused))) 117 | { 118 | cleri_rule_store_t nrule; 119 | cleri_node_t * node; 120 | cleri_node_t * rnode; 121 | 122 | if (pr->flags & CLERI_FLAG_EXCLUDE_RULE_THIS) 123 | { 124 | nrule.depth = 0; 125 | nrule.tested.str = NULL; 126 | nrule.tested.node = NULL; 127 | nrule.tested.next = NULL; 128 | nrule.root_obj = cl_obj->via.rule->cl_obj; 129 | 130 | node = cleri__parse_walk( 131 | pr, 132 | parent, 133 | nrule.root_obj, 134 | &nrule, 135 | CLERI__EXP_MODE_REQUIRED); 136 | 137 | if (node != NULL) 138 | { 139 | node = parent; 140 | } 141 | 142 | /* cleanup rule */ 143 | rule__tested_free(&nrule.tested); 144 | 145 | return node; 146 | } 147 | 148 | if ((node = cleri__node_new(cl_obj, parent->str + parent->len, 0)) == NULL) 149 | { 150 | pr->is_valid = -1; 151 | return NULL; 152 | } 153 | 154 | nrule.depth = 0; 155 | nrule.tested.str = NULL; 156 | nrule.tested.node = NULL; 157 | nrule.tested.next = NULL; 158 | nrule.root_obj = cl_obj->via.rule->cl_obj; 159 | 160 | rnode = cleri__parse_walk( 161 | pr, 162 | node, 163 | nrule.root_obj, 164 | &nrule, 165 | CLERI__EXP_MODE_REQUIRED); 166 | 167 | if (rnode == NULL) 168 | { 169 | cleri__node_free(node); 170 | node = NULL; 171 | } 172 | else 173 | { 174 | parent->len += node->len; 175 | cleri__node_add(parent, node); 176 | } 177 | 178 | /* cleanup rule */ 179 | rule__tested_free(&nrule.tested); 180 | 181 | return node; 182 | } 183 | 184 | /* 185 | * Cleanup rule tested 186 | */ 187 | static void rule__tested_free(cleri_rule_tested_t * tested) 188 | { 189 | cleri_rule_tested_t * next = tested->next; 190 | cleri__node_free(tested->node); 191 | while (next != NULL) 192 | { 193 | tested = next->next; 194 | cleri__node_free(next->node); 195 | free(next); 196 | next = tested; 197 | } 198 | } 199 | 200 | -------------------------------------------------------------------------------- /src/sequence.c: -------------------------------------------------------------------------------- 1 | /* 2 | * sequence.c - cleri sequence element. 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static void sequence__free(cleri_t * cl_object); 10 | static cleri_node_t * sequence__parse( 11 | cleri_parse_t * pr, 12 | cleri_node_t * parent, 13 | cleri_t * cl_obj, 14 | cleri_rule_store_t * rule); 15 | 16 | /* 17 | * Returns NULL and in case an error has occurred. 18 | */ 19 | cleri_t * cleri_sequence(uint32_t gid, size_t len, ...) 20 | { 21 | va_list ap; 22 | cleri_t * cl_object = cleri_new( 23 | gid, 24 | CLERI_TP_SEQUENCE, 25 | &sequence__free, 26 | &sequence__parse); 27 | 28 | if (cl_object == NULL) 29 | { 30 | return NULL; 31 | } 32 | 33 | cl_object->via.sequence = cleri__malloc(cleri_sequence_t); 34 | 35 | if (cl_object->via.sequence == NULL) 36 | { 37 | free(cl_object); 38 | return NULL; 39 | } 40 | 41 | cl_object->via.sequence->olist = cleri__olist_new(); 42 | 43 | if (cl_object->via.sequence->olist == NULL) 44 | { 45 | cleri_free(cl_object); 46 | return NULL; 47 | } 48 | 49 | va_start(ap, len); 50 | while(len--) 51 | { 52 | if (cleri__olist_append( 53 | cl_object->via.sequence->olist, 54 | va_arg(ap, cleri_t *))) 55 | { 56 | cleri__olist_cancel(cl_object->via.sequence->olist); 57 | cleri_free(cl_object); 58 | cl_object = NULL; 59 | break; 60 | } 61 | } 62 | va_end(ap); 63 | 64 | return cl_object; 65 | } 66 | 67 | /* 68 | * Destroy sequence object. 69 | */ 70 | static void sequence__free(cleri_t * cl_object) 71 | { 72 | cleri__olist_free(cl_object->via.sequence->olist); 73 | free(cl_object->via.sequence); 74 | } 75 | 76 | /* 77 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 78 | */ 79 | static cleri_node_t * sequence__parse( 80 | cleri_parse_t * pr, 81 | cleri_node_t * parent, 82 | cleri_t * cl_obj, 83 | cleri_rule_store_t * rule) 84 | { 85 | cleri_olist_t * olist; 86 | cleri_node_t * node; 87 | cleri_node_t * rnode; 88 | 89 | olist = cl_obj->via.sequence->olist; 90 | if ((node = cleri__node_new(cl_obj, parent->str + parent->len, 0)) == NULL) 91 | { 92 | pr->is_valid = -1; 93 | return NULL; 94 | } 95 | 96 | while (olist != NULL) 97 | { 98 | rnode = cleri__parse_walk( 99 | pr, 100 | node, 101 | olist->cl_obj, 102 | rule, 103 | CLERI__EXP_MODE_REQUIRED); 104 | if (rnode == NULL) 105 | { 106 | cleri__node_free(node); 107 | return NULL; 108 | } 109 | olist = olist->next; 110 | } 111 | 112 | parent->len += node->len; 113 | cleri__node_add(parent, node); 114 | return node; 115 | } 116 | -------------------------------------------------------------------------------- /src/this.c: -------------------------------------------------------------------------------- 1 | /* 2 | * this.c - cleri THIS element. there should be only one single instance 3 | * of this which can even be shared over different grammars. 4 | * Always use this element using its constant CLERI_THIS and 5 | * somewhere within a prio element. 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static cleri_node_t * cleri_parse_this( 13 | cleri_parse_t * pr, 14 | cleri_node_t * parent, 15 | cleri_t * cl_obj, 16 | cleri_rule_store_t * rule); 17 | 18 | static int cleri_dummy = 0; 19 | 20 | static cleri_t cleri_this = { 21 | .gid=0, 22 | .ref=1, 23 | .free_object=NULL, 24 | .parse_object=&cleri_parse_this, 25 | .tp=CLERI_TP_THIS, 26 | .via={.dummy=(void *) &cleri_dummy}}; 27 | 28 | cleri_t * CLERI_THIS = &cleri_this; 29 | 30 | /* 31 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 32 | */ 33 | static cleri_node_t * cleri_parse_this( 34 | cleri_parse_t * pr, 35 | cleri_node_t * parent, 36 | cleri_t * cl_obj, 37 | cleri_rule_store_t * rule) 38 | { 39 | cleri_node_t * node; 40 | cleri_rule_tested_t * tested; 41 | const char * str = parent->str + parent->len; 42 | 43 | switch (cleri__rule_init(&tested, &rule->tested, str)) 44 | { 45 | case CLERI_RULE_TRUE: 46 | if (pr->flags & CLERI_FLAG_EXCLUDE_RULE_THIS) 47 | { 48 | tested->node = cleri__parse_walk( 49 | pr, 50 | parent, 51 | rule->root_obj, 52 | rule, 53 | CLERI__EXP_MODE_REQUIRED); 54 | 55 | return tested->node == NULL ? NULL : parent; 56 | } 57 | if ((node = cleri__node_new(cl_obj, str, 0)) == NULL) 58 | { 59 | pr->is_valid = -1; 60 | return NULL; 61 | } 62 | assert(tested->node == NULL); 63 | tested->node = cleri__parse_walk( 64 | pr, 65 | node, 66 | rule->root_obj, 67 | rule, 68 | CLERI__EXP_MODE_REQUIRED); 69 | if (tested->node == NULL) 70 | { 71 | cleri__node_free(node); 72 | return NULL; 73 | } 74 | break; 75 | case CLERI_RULE_FALSE: 76 | node = tested->node; 77 | if (node == NULL) 78 | { 79 | return NULL; 80 | } 81 | if (node->next) 82 | { 83 | /* It is only required to duplicate when a sibling is set. 84 | * If no sibling is set, then either this node will win over the 85 | * previous parent but then the sibling will be kept, or the parent 86 | * will win, but then we should end up with no sibling 87 | */ 88 | node = cleri__node_dup(node); 89 | if (node == NULL) 90 | { 91 | pr->is_valid = -1; 92 | return NULL; 93 | } 94 | } 95 | else 96 | { 97 | node->ref++; 98 | } 99 | break; 100 | case CLERI_RULE_ERROR: 101 | pr->is_valid = -1; 102 | return NULL; 103 | 104 | default: 105 | assert (0); 106 | return NULL; 107 | } 108 | 109 | parent->len += node->len; 110 | cleri__node_add(parent, node); 111 | return node; 112 | } 113 | -------------------------------------------------------------------------------- /src/token.c: -------------------------------------------------------------------------------- 1 | /* 2 | * token.c - cleri token element. note that one single char will parse 3 | * slightly faster compared to tokens containing more characters. 4 | * (be careful a token should not match the keyword regular 5 | * expression) 6 | */ 7 | #include 8 | #include 9 | #include 10 | 11 | static void token__free(cleri_t * cl_object); 12 | static cleri_node_t * token__parse( 13 | cleri_parse_t * pr, 14 | cleri_node_t * parent, 15 | cleri_t * cl_obj, 16 | cleri_rule_store_t * rule); 17 | 18 | /* 19 | * Returns NULL in case an error has occurred. 20 | */ 21 | cleri_t * cleri_token(uint32_t gid, const char * token) 22 | { 23 | cleri_t * cl_object = cleri_new( 24 | gid, 25 | CLERI_TP_TOKEN, 26 | &token__free, 27 | &token__parse); 28 | 29 | 30 | if (cl_object == NULL) 31 | { 32 | return NULL; 33 | } 34 | 35 | cl_object->via.token = cleri__malloc(cleri_token_t); 36 | 37 | if (cl_object->via.token == NULL) 38 | { 39 | free(cl_object); 40 | return NULL; 41 | } 42 | 43 | cl_object->via.token->token = token; 44 | cl_object->via.token->len = strlen(token); 45 | 46 | return cl_object; 47 | } 48 | 49 | /* 50 | * Destroy token object. 51 | */ 52 | static void token__free(cleri_t * cl_object) 53 | { 54 | free(cl_object->via.token); 55 | } 56 | 57 | /* 58 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 59 | */ 60 | static cleri_node_t * token__parse( 61 | cleri_parse_t * pr, 62 | cleri_node_t * parent, 63 | cleri_t * cl_obj, 64 | cleri_rule_store_t * rule __attribute__((unused))) 65 | { 66 | cleri_node_t * node = NULL; 67 | const char * str = parent->str + parent->len; 68 | if (strncmp( 69 | cl_obj->via.token->token, 70 | str, 71 | cl_obj->via.token->len) == 0) 72 | { 73 | if ((node = cleri__node_new( 74 | cl_obj, 75 | str, 76 | cl_obj->via.token->len)) != NULL) 77 | { 78 | parent->len += node->len; 79 | cleri__node_add(parent, node); 80 | } 81 | else 82 | { 83 | pr->is_valid = -1; 84 | } 85 | } 86 | else if (cleri__expecting_update(pr->expecting, cl_obj, str) == -1) 87 | { 88 | pr->is_valid = -1; 89 | } 90 | return node; 91 | } 92 | 93 | -------------------------------------------------------------------------------- /src/tokens.c: -------------------------------------------------------------------------------- 1 | /* 2 | * tokens.c - cleri tokens element. (like token but can contain more tokens 3 | * in one element) 4 | */ 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static void tokens__free(cleri_t * cl_object); 13 | static cleri_node_t * tokens__parse( 14 | cleri_parse_t * pr, 15 | cleri_node_t * parent, 16 | cleri_t * cl_obj, 17 | cleri_rule_store_t * rule); 18 | static int tokens__list_add( 19 | cleri_tlist_t ** tlist, 20 | const char * token, 21 | size_t len); 22 | static void tokens__list_str(cleri_tlist_t * tlist, char * s); 23 | static void tokens__list_free(cleri_tlist_t * tlist); 24 | 25 | /* 26 | * Returns NULL in case an error has occurred. 27 | */ 28 | cleri_t * cleri_tokens(uint32_t gid, const char * tokens) 29 | { 30 | size_t len; 31 | char * pt; 32 | cleri_t * cl_object; 33 | 34 | cl_object = cleri_new( 35 | gid, 36 | CLERI_TP_TOKENS, 37 | &tokens__free, 38 | &tokens__parse); 39 | 40 | if (cl_object == NULL) 41 | { 42 | return NULL; 43 | } 44 | 45 | cl_object->via.tokens = cleri__malloc(cleri_tokens_t); 46 | 47 | if (cl_object->via.tokens == NULL) 48 | { 49 | free(cl_object); 50 | return NULL; 51 | } 52 | 53 | /* copy the sting twice, first one we set spaces to 0...*/ 54 | cl_object->via.tokens->tokens = strdup(tokens); 55 | 56 | /* ...and this one we keep for showing a spaced version */ 57 | cl_object->via.tokens->spaced = (char *) malloc(strlen(tokens) + 1); 58 | 59 | cl_object->via.tokens->tlist = cleri__malloc(cleri_tlist_t); 60 | 61 | 62 | if ( cl_object->via.tokens->tokens == NULL || 63 | cl_object->via.tokens->spaced == NULL || 64 | cl_object->via.tokens->tlist == NULL) 65 | { 66 | cleri_free(cl_object); 67 | return NULL; 68 | } 69 | 70 | cl_object->via.tokens->tlist->token = NULL; 71 | cl_object->via.tokens->tlist->next = NULL; 72 | cl_object->via.tokens->tlist->len = 0; 73 | 74 | pt = cl_object->via.tokens->tokens; 75 | 76 | for (len = 0;; pt++) 77 | { 78 | if (!*pt || isspace(*pt)) 79 | { 80 | if (len) 81 | { 82 | if (tokens__list_add( 83 | &cl_object->via.tokens->tlist, 84 | pt - len, 85 | len)) 86 | { 87 | cleri_free(cl_object); 88 | return NULL; 89 | } 90 | len = 0; 91 | } 92 | if (*pt) 93 | *pt = 0; 94 | else 95 | { 96 | break; 97 | } 98 | } 99 | else 100 | { 101 | len++; 102 | } 103 | } 104 | 105 | /* tlist->token is never empty */ 106 | assert (cl_object->via.tokens->tlist->token != NULL); 107 | 108 | tokens__list_str( 109 | cl_object->via.tokens->tlist, 110 | cl_object->via.tokens->spaced); 111 | 112 | return cl_object; 113 | } 114 | 115 | /* 116 | * Destroy token object. 117 | */ 118 | static void tokens__free(cleri_t * cl_object) 119 | { 120 | tokens__list_free(cl_object->via.tokens->tlist); 121 | free(cl_object->via.tokens->tokens); 122 | free(cl_object->via.tokens->spaced); 123 | free(cl_object->via.tokens); 124 | } 125 | 126 | /* 127 | * Returns a node or NULL. In case of an error pr->is_valid is set to -1. 128 | */ 129 | static cleri_node_t * tokens__parse( 130 | cleri_parse_t * pr, 131 | cleri_node_t * parent, 132 | cleri_t * cl_obj, 133 | cleri_rule_store_t * rule __attribute__((unused))) 134 | { 135 | cleri_node_t * node = NULL; 136 | const char * str = parent->str + parent->len; 137 | cleri_tlist_t * tlist = cl_obj->via.tokens->tlist; 138 | 139 | /* we can trust that at least one token is in the list */ 140 | for (; tlist != NULL; tlist = tlist->next) 141 | { 142 | if (strncmp(tlist->token, str, tlist->len) == 0) 143 | { 144 | if ((node = cleri__node_new(cl_obj, str, tlist->len)) != NULL) 145 | { 146 | parent->len += node->len; 147 | cleri__node_add(parent, node); 148 | } 149 | else 150 | { 151 | pr->is_valid = -1; 152 | } 153 | return node; 154 | } 155 | } 156 | if (cleri__expecting_update(pr->expecting, cl_obj, str) == -1) 157 | { 158 | pr->is_valid = -1; 159 | } 160 | return NULL; 161 | } 162 | 163 | /* 164 | * Returns 0 if successful and -1 in case an error occurred. 165 | * (the token list remains unchanged in case of an error) 166 | * 167 | * The token will be placed in the list based on the length. Thus the token 168 | * list remains ordered on length. (largest first) 169 | */ 170 | static int tokens__list_add( 171 | cleri_tlist_t ** tlist, 172 | const char * token, 173 | size_t len) 174 | { 175 | cleri_tlist_t * tmp, * prev, * current; 176 | current = prev = *tlist; 177 | 178 | if (current->token == NULL) 179 | { 180 | current->token = token; 181 | current->len = len; 182 | return 0; 183 | } 184 | tmp = cleri__malloc(cleri_tlist_t); 185 | if (tmp == NULL) 186 | { 187 | return -1; 188 | } 189 | tmp->len = len; 190 | tmp->token = token; 191 | 192 | while (current != NULL && len <= current->len) 193 | { 194 | prev = current; 195 | current = current->next; 196 | } 197 | 198 | if (current == *tlist) 199 | { 200 | tmp->next = *tlist; 201 | *tlist = tmp; 202 | } 203 | else 204 | { 205 | tmp->next = current; 206 | prev->next = tmp; 207 | } 208 | 209 | return 0; 210 | } 211 | 212 | /* 213 | * Copies tokens to spaced. This is the input tokes orders by their length. 214 | * No errors can occur since in any case enough space is allocated for *s. 215 | */ 216 | static void tokens__list_str(cleri_tlist_t * tlist, char * s) 217 | { 218 | assert (tlist->token != NULL); 219 | memcpy(s, tlist->token, tlist->len); 220 | s += tlist->len; 221 | while ((tlist = tlist->next) != NULL) 222 | { 223 | *s = ' '; 224 | s++; 225 | memcpy(s, tlist->token, tlist->len); 226 | s += tlist->len; 227 | } 228 | *s = '\0'; 229 | } 230 | 231 | /* 232 | * Destroy token list. (NULL can be parsed tlist argument) 233 | */ 234 | static void tokens__list_free(cleri_tlist_t * tlist) 235 | { 236 | cleri_tlist_t * next; 237 | while (tlist != NULL) 238 | { 239 | next = tlist->next; 240 | free(tlist); 241 | tlist = next; 242 | } 243 | } 244 | 245 | -------------------------------------------------------------------------------- /src/version.c: -------------------------------------------------------------------------------- 1 | /* 2 | * version.c - cleri version information. 3 | */ 4 | 5 | #include 6 | 7 | const char * cleri_version(void) 8 | { 9 | return LIBCLERI_VERSION; 10 | } -------------------------------------------------------------------------------- /test/helpers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static char * parse_str( 5 | cleri_parse_t * pr, 6 | cleri_translate_t * translate) __attribute__((unused)); 7 | 8 | static char * parse_str(cleri_parse_t * pr, cleri_translate_t * translate) 9 | { 10 | size_t sz; 11 | int i = cleri_parse_strn(NULL, 0, pr, translate); 12 | if (i < 0) 13 | { 14 | return NULL; 15 | } 16 | sz = i + 1; 17 | char * s = (char *) malloc(sz); 18 | if (s) 19 | { 20 | i = cleri_parse_strn(s, sz, pr, translate); 21 | if (i < 0 || i >= (int) sz) 22 | { 23 | free(s); 24 | return NULL; 25 | } 26 | } 27 | return s; 28 | } 29 | 30 | #define _assert_is_valid(__grammar, __str) \ 31 | { \ 32 | cleri_parse_t * __pr = cleri_parse2(__grammar, __str, 0); \ 33 | _assert (__pr); \ 34 | _assert (__pr->is_valid); \ 35 | cleri_parse_free(__pr); \ 36 | } 37 | 38 | #define _assert_is_not_valid(__grammar, __str) \ 39 | { \ 40 | cleri_parse_t * __pr = cleri_parse(__grammar, __str); \ 41 | _assert (__pr); \ 42 | _assert (!__pr->is_valid); \ 43 | cleri_parse_free(__pr); \ 44 | } 45 | 46 | #define _assert_is_valid_flags(__grammar, __str, __flags) \ 47 | { \ 48 | cleri_parse_t * __pr = cleri_parse2(__grammar, __str, __flags); \ 49 | _assert (__pr); \ 50 | _assert (__pr->is_valid); \ 51 | cleri_parse_free(__pr); \ 52 | } 53 | 54 | #define _assert_parse_str(__grammar, __str, __expect, __translate) \ 55 | { \ 56 | cleri_parse_t * __pr = cleri_parse2(__grammar, __str, 0); \ 57 | char * __s = parse_str(__pr, __translate); \ 58 | _assert (__s); \ 59 | if (strcmp(__s, __expect) != 0) printf("\n\ngot: `%s`\n", __s); \ 60 | _assert (strcmp(__s, __expect) == 0); \ 61 | free(__s); \ 62 | if (__pr) cleri_parse_free(__pr); \ 63 | } 64 | 65 | #define _assert_parse_str2(__grammar, __str, __expect, __translate) \ 66 | { \ 67 | cleri_parse_t * __pr = cleri_parse2(__grammar, __str, 1); \ 68 | char * __s = parse_str(__pr, __translate); \ 69 | _assert (__s); \ 70 | if (strcmp(__s, __expect) != 0) printf("\n\ngot: `%s`\n", __s); \ 71 | _assert (strcmp(__s, __expect) == 0); \ 72 | free(__s); \ 73 | if (__pr) cleri_parse_free(__pr); \ 74 | } 75 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #ifndef CLERI_TEST_H_ 2 | #define CLERI_TEST_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define TEST_OK 0 10 | #define TEST_FAILED 1 11 | #define TEST_MSG_OK "....\x1B[32mOK\x1B[0m" 12 | #define TEST_MSG_FAILED "\x1B[31mFAILED\x1B[0m" 13 | 14 | static struct timeval start; 15 | static struct timeval end; 16 | 17 | static int status = TEST_OK; 18 | static int count = 0; 19 | 20 | const char * padding = 21 | ".............................." 22 | ".............................."; 23 | 24 | static void test_start(char * test_name) 25 | { 26 | count = 0; 27 | int padlen = 60 - strlen(test_name); 28 | printf("Testing %s%*.*s", test_name, padlen, padlen, padding); 29 | gettimeofday(&start, 0); 30 | } 31 | 32 | static int test_end(void) 33 | { 34 | gettimeofday(&end, 0); 35 | float t = (end.tv_sec - start.tv_sec) * 1000.0f + 36 | (end.tv_usec - start.tv_usec) / 1000.0f; 37 | 38 | printf("%s (%.3f ms)\n", 39 | (status == TEST_OK) ? TEST_MSG_OK : TEST_MSG_FAILED, 40 | t); 41 | 42 | return status; 43 | } 44 | 45 | #define _assert(e) (void)((e)?count++:(status = TEST_FAILED) && printf("\n\x1B[33mAssertion failed (%s:%d):\x1B[0m %s\n\n", __FILE__, __LINE__, #e)) 46 | 47 | #endif /* CLERI_TEST_H_ */ -------------------------------------------------------------------------------- /test/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | RET=0 3 | echo -n "Test using valgrind for memory errors and leaks: " 4 | if [[ "$NOMEMTEST" -ne "1" ]] && hash valgrind 2>/dev/null; then 5 | NOMEMTEST=0; 6 | echo -e "\x1B[32menabled\x1B[0m"; 7 | else 8 | NOMEMTEST=1; 9 | echo -e "\x1B[33mdisabled\x1B[0m"; 10 | fi 11 | 12 | 13 | 14 | run () { 15 | if [ ! -f $1/sources ]; then 16 | return; 17 | fi 18 | 19 | if [[ "$USELIB" -eq "1" ]]; then 20 | LSOURCE=-lcleri 21 | else 22 | LSOURCE=$(cat $1/sources) 23 | fi 24 | 25 | SOURCE=$1/$1.c 26 | OUT=$1.out 27 | rm "$OUT" 2> /dev/null 28 | 29 | gcc -I"../inc" -O0 -g3 -Wall -Wextra -Winline -std=gnu89 $SOURCE $LSOURCE -lm -lpcre2-8 -o "$OUT" 30 | if [[ "$NOMEMTEST" -ne "1" ]]; then 31 | valgrind --tool=memcheck --error-exitcode=1 --leak-check=full -q ./$OUT 32 | else 33 | ./$OUT 34 | fi 35 | rc=$?; if [[ $rc != 0 ]]; then RET=$((RET+1)); fi 36 | rm "$OUT" 2> /dev/null 37 | rm -r "$OUT.dSYM" 2> /dev/null 38 | } 39 | 40 | if [ $# -eq 0 ]; then 41 | for d in test_*/ ; do 42 | run "${d%?}" 43 | done 44 | else 45 | name=`echo $1 | sed 's/\(test_\)\?\(.*\?\)$/\2/g' | sed 's/\(.*\)\/$/\1/g'` 46 | run "test_$name" 47 | fi 48 | 49 | exit $RET -------------------------------------------------------------------------------- /test/test_choice/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/sequence.c 10 | ../src/choice.c 11 | -------------------------------------------------------------------------------- /test/test_choice/test_choice.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_choice_most_greedy(void) 6 | { 7 | test_start("choice (most_greedy)"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * k_iris, * seq, * choice; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | k_iris = cleri_keyword(0, "iris", false); 14 | seq = cleri_sequence(0, 2, k_hi, k_iris); 15 | choice = cleri_choice(0, true, 2, k_hi, seq); 16 | grammar = cleri_grammar(choice, NULL); 17 | 18 | _assert (choice->via.choice->most_greedy == true); 19 | _assert_is_valid (grammar, "hi"); 20 | _assert_is_valid (grammar, "hi iris"); 21 | _assert_is_not_valid (grammar, "hi sasha"); 22 | 23 | cleri_grammar_free(grammar); 24 | 25 | return test_end(); 26 | } 27 | 28 | static int test_choice_first_match(void) 29 | { 30 | test_start("choice (first_match)"); 31 | 32 | cleri_grammar_t * grammar; 33 | cleri_t * k_hi, * k_iris, * seq, * choice; 34 | 35 | k_hi = cleri_keyword(0, "hi", false); 36 | k_iris = cleri_keyword(0, "iris", false); 37 | seq = cleri_sequence(0, 2, k_hi, k_iris); 38 | choice = cleri_choice(0, false, 2, k_hi, seq); 39 | grammar = cleri_grammar(choice, NULL); 40 | 41 | _assert (choice->via.choice->most_greedy == false); 42 | _assert_is_valid (grammar, "hi"); 43 | _assert_is_not_valid (grammar, "hi iris"); 44 | _assert_is_not_valid (grammar, "hi sasha"); 45 | 46 | 47 | cleri_grammar_free(grammar); 48 | 49 | return test_end(); 50 | } 51 | 52 | static int test_choice_expecting(void) 53 | { 54 | test_start("choice (expecting)"); 55 | 56 | cleri_grammar_t * grammar; 57 | cleri_t * k_hi, * k_iris, * k_sasha, * seq, * choice; 58 | 59 | k_hi = cleri_keyword(0, "hi", false); 60 | k_iris = cleri_keyword(0, "iris", false); 61 | k_sasha = cleri_keyword(0, "sasha", false); 62 | choice = cleri_choice(0, false, 2, k_iris, k_sasha); 63 | seq = cleri_sequence(0, 2, k_hi, choice); 64 | grammar = cleri_grammar(seq, NULL); 65 | 66 | _assert (choice->via.choice->most_greedy == false); 67 | _assert_is_valid (grammar, "hi iris"); 68 | _assert_is_valid (grammar, "hi sasha"); 69 | _assert_is_not_valid (grammar, "hi cleri"); 70 | 71 | _assert_parse_str ( 72 | grammar, 73 | "hi cleri", 74 | "error at line 1, position 3, " 75 | "unexpected `cleri`, expecting: iris or sasha", 76 | NULL); 77 | 78 | cleri_grammar_free(grammar); 79 | 80 | return test_end(); 81 | } 82 | 83 | int main() 84 | { 85 | return ( 86 | test_choice_most_greedy() || 87 | test_choice_first_match() || 88 | test_choice_expecting() || 89 | 0 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /test/test_dup/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/sequence.c 10 | ../src/dup.c 11 | -------------------------------------------------------------------------------- /test/test_dup/test_dup.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | #define ORIG 0 5 | #define DUPL 1 6 | 7 | static const char * translate(cleri_t * o) 8 | { 9 | switch (o->gid) 10 | { 11 | case DUPL: 12 | return "hi(dup)"; 13 | } 14 | return NULL; 15 | }; 16 | 17 | static int test_dup(void) 18 | { 19 | test_start("dup"); 20 | 21 | cleri_grammar_t * grammar; 22 | cleri_t * k_hi, * dup; 23 | 24 | k_hi = cleri_keyword(ORIG, "hi", false); 25 | dup = cleri_dup(DUPL, k_hi); 26 | grammar = cleri_grammar(cleri_sequence(0, 2, k_hi, dup), NULL); 27 | 28 | _assert (k_hi->gid == ORIG); 29 | _assert (dup->gid == DUPL); 30 | _assert_is_valid (grammar, "hi hi"); 31 | _assert_is_not_valid (grammar, "hi"); 32 | _assert_parse_str ( 33 | grammar, 34 | "", 35 | "error at line 1, position 0, expecting: hi", 36 | &translate); 37 | _assert_parse_str ( 38 | grammar, 39 | "hi", 40 | "error at line 1, position 2, expecting: hi(dup)", 41 | &translate); 42 | _assert_parse_str2 ( 43 | grammar, 44 | "", 45 | "error at line 1, position 0", 46 | &translate); 47 | _assert_parse_str2 ( 48 | grammar, 49 | "hi", 50 | "error at line 1, position 2", 51 | &translate); 52 | cleri_grammar_free(grammar); 53 | 54 | return test_end(); 55 | } 56 | 57 | 58 | int main() 59 | { 60 | return ( 61 | test_dup() || 62 | 0 63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /test/test_json_lang/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/choice.c 9 | ../src/keyword.c 10 | ../src/list.c 11 | ../src/ref.c 12 | ../src/regex.c 13 | ../src/sequence.c 14 | ../src/token.c 15 | -------------------------------------------------------------------------------- /test/test_json_lang/test_json_lang.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | enum cleri_grammar_ids { 5 | CLERI_NONE, // used for objects with no name 6 | CLERI_GID_JSON_ARRAY, 7 | CLERI_GID_JSON_MAP, 8 | CLERI_GID_JSON_MAP_ITEM, 9 | CLERI_GID_K_FALSE, 10 | CLERI_GID_K_NULL, 11 | CLERI_GID_K_TRUE, 12 | CLERI_GID_R_FLOAT, 13 | CLERI_GID_R_INTEGER, 14 | CLERI_GID_R_STRING, 15 | CLERI_GID_START, 16 | CLERI_END // can be used to get the enum length 17 | }; 18 | 19 | #define CLERI_CASE_SENSITIVE 0 20 | #define CLERI_CASE_INSENSITIVE 1 21 | 22 | #define CLERI_FIRST_MATCH 0 23 | #define CLERI_MOST_GREEDY 1 24 | 25 | cleri_grammar_t * compile_grammar(void) 26 | { 27 | cleri_t * START = cleri_ref(); 28 | cleri_t * r_string = cleri_regex(CLERI_GID_R_STRING, "^(\")(?:(?=(\\\\?))\\2.)*?\\1"); 29 | cleri_t * r_float = cleri_regex(CLERI_GID_R_FLOAT, "^-?[0-9]+\\.?[0-9]+"); 30 | cleri_t * r_integer = cleri_regex(CLERI_GID_R_INTEGER, "^-?[0-9]+"); 31 | cleri_t * k_true = cleri_keyword(CLERI_GID_K_TRUE, "true", CLERI_CASE_SENSITIVE); 32 | cleri_t * k_false = cleri_keyword(CLERI_GID_K_FALSE, "false", CLERI_CASE_SENSITIVE); 33 | cleri_t * k_null = cleri_keyword(CLERI_GID_K_NULL, "null", CLERI_CASE_SENSITIVE); 34 | cleri_t * json_map_item = cleri_sequence( 35 | CLERI_GID_JSON_MAP_ITEM, 36 | 3, 37 | r_string, 38 | cleri_token(CLERI_NONE, ":"), 39 | START 40 | ); 41 | cleri_t * json_map = cleri_sequence( 42 | CLERI_GID_JSON_MAP, 43 | 3, 44 | cleri_token(CLERI_NONE, "{"), 45 | cleri_list(CLERI_NONE, json_map_item, cleri_token(CLERI_NONE, ","), 0, 0, 0), 46 | cleri_token(CLERI_NONE, "}") 47 | ); 48 | cleri_t * json_array = cleri_sequence( 49 | CLERI_GID_JSON_ARRAY, 50 | 3, 51 | cleri_token(CLERI_NONE, "["), 52 | cleri_list(CLERI_NONE, START, cleri_token(CLERI_NONE, ","), 0, 0, 0), 53 | cleri_token(CLERI_NONE, "]") 54 | ); 55 | cleri_ref_set(START, cleri_choice( 56 | CLERI_GID_START, 57 | CLERI_MOST_GREEDY, 58 | 8, 59 | r_string, 60 | r_float, 61 | r_integer, 62 | k_true, 63 | k_false, 64 | k_null, 65 | json_map, 66 | json_array 67 | )); 68 | 69 | cleri_grammar_t * grammar = cleri_grammar(START, "^\\w+"); 70 | 71 | return grammar; 72 | } 73 | 74 | 75 | static int test_json_lang(void) 76 | { 77 | test_start("json (language)"); 78 | 79 | cleri_grammar_t * grammar = compile_grammar(); 80 | _assert (grammar); 81 | 82 | _assert_is_valid (grammar, "{\"json\": [1, 2, 3]}"); 83 | _assert_is_valid (grammar, "{\"json\": {\"isOk\": true}}"); 84 | _assert_is_not_valid (grammar, "{\"json\": [1, 2, 3]"); 85 | 86 | cleri_grammar_free(grammar); 87 | 88 | return test_end(); 89 | } 90 | 91 | 92 | int main() 93 | { 94 | return ( 95 | test_json_lang() || 96 | 0 97 | ); 98 | } -------------------------------------------------------------------------------- /test/test_keyword/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/token.c 10 | ../src/choice.c 11 | -------------------------------------------------------------------------------- /test/test_keyword/test_keyword.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_keyword(void) 6 | { 7 | test_start("keyword"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | _assert (k_hi); 14 | grammar = cleri_grammar(k_hi, NULL); 15 | _assert (grammar); 16 | 17 | _assert (k_hi->gid == 0); 18 | _assert (k_hi->via.keyword->ign_case == false); 19 | _assert_is_valid (grammar, "hi"); 20 | _assert_is_valid (grammar, " hi "); 21 | _assert_is_not_valid (grammar, "Hi"); 22 | _assert_is_not_valid (grammar, "hello"); 23 | _assert_parse_str ( 24 | grammar, 25 | "hello", 26 | "error at line 1, position 0, unexpected `hello`, expecting: hi", 27 | NULL); 28 | _assert_parse_str ( 29 | grammar, 30 | "hi", 31 | "parsed successfully", 32 | NULL); 33 | _assert_parse_str2 ( 34 | grammar, 35 | "hello", 36 | "error at line 1, position 0, unexpected `hello`", 37 | NULL); 38 | _assert_parse_str2 ( 39 | grammar, 40 | "hi", 41 | "parsed successfully", 42 | NULL); 43 | cleri_grammar_free(grammar); 44 | 45 | return test_end(); 46 | } 47 | 48 | static int test_keyword_ign_case(void) 49 | { 50 | test_start("keyword (ign_case)"); 51 | 52 | cleri_grammar_t * grammar; 53 | cleri_t * k_hi; 54 | 55 | k_hi = cleri_keyword(1, "hi", true); 56 | _assert (k_hi); 57 | grammar = cleri_grammar(k_hi, NULL); 58 | _assert (grammar); 59 | 60 | _assert (k_hi->gid == 1); 61 | _assert (k_hi->via.keyword->ign_case == true); 62 | _assert_is_valid (grammar, "hi"); 63 | _assert_is_valid (grammar, "Hi"); 64 | _assert_is_not_valid (grammar, "hello"); 65 | _assert_is_not_valid (grammar, 66 | "hihihihihihihihihihihihihihihihi" 67 | "hihihihihihihihihihihihihihihihi" 68 | "hihihihihihihihihihihihihihihihi" 69 | "hihihihihihihihihihihihihihihihi" 70 | "hihihihihihihihihihihihihihihihi" 71 | "hihihihihihihihihihihihihihihihi" 72 | "hihihihihihihihihihihihihihihihi" 73 | "hihihihihihihihihihihihihihihihi"); 74 | _assert_parse_str ( 75 | grammar, 76 | "Hi Iris", 77 | "error at line 1, position 2, expecting: end_of_statement", 78 | NULL); 79 | _assert_parse_str2 ( 80 | grammar, 81 | "Hi Iris", 82 | "error at line 1, position 2", 83 | NULL); 84 | cleri_grammar_free(grammar); 85 | 86 | return test_end(); 87 | } 88 | 89 | static int test_keyword_alt_regkw(void) 90 | { 91 | test_start("keyword (alt_regkw)"); 92 | 93 | cleri_grammar_t * grammar; 94 | cleri_t * k_hi, * t_hi; 95 | 96 | k_hi = cleri_keyword(0, "hi", false); 97 | t_hi = cleri_token(0, "HI"); 98 | 99 | grammar = cleri_grammar(cleri_choice(0, 1, 2, k_hi, t_hi), "^[a-z]+"); 100 | _assert (grammar); 101 | 102 | _assert_is_valid (grammar, "hi"); 103 | _assert_is_valid (grammar, "HI"); 104 | _assert_is_not_valid (grammar, "Hi"); 105 | 106 | cleri_grammar_free(grammar); 107 | 108 | return test_end(); 109 | } 110 | 111 | int main() 112 | { 113 | return ( 114 | test_keyword() || 115 | test_keyword_ign_case() || 116 | test_keyword_alt_regkw() || 117 | 0 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /test/test_list/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/token.c 10 | ../src/list.c 11 | ../src/choice.c -------------------------------------------------------------------------------- /test/test_list/test_list.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_list(void) 6 | { 7 | test_start("list"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * delimiter, * list; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | delimiter = cleri_token(0, ","); 14 | list = cleri_list(0, k_hi, delimiter, 0, 0, false); 15 | grammar = cleri_grammar(list, NULL); 16 | 17 | // assert statements 18 | _assert (list->via.list->min == 0); 19 | _assert (list->via.list->max == 0); 20 | _assert (list->via.list->opt_closing == false); 21 | _assert_is_valid (grammar, "hi, hi, hi"); 22 | _assert_is_valid (grammar, "hi"); 23 | _assert_is_valid (grammar, ""); 24 | _assert_is_not_valid (grammar, "hi,"); 25 | _assert_parse_str ( 26 | grammar, 27 | "hi.", 28 | "error at line 1, position 2, " 29 | "unexpected character `.`, expecting: , or end_of_statement", 30 | NULL); 31 | _assert_parse_str2 ( 32 | grammar, 33 | "hi.", 34 | "error at line 1, position 2, unexpected character `.`", 35 | NULL); 36 | 37 | /* check if list children is really NULL */ 38 | { 39 | cleri_parse_t * pr = cleri_parse(grammar, ""); 40 | _assert (pr); 41 | _assert (pr->is_valid); 42 | _assert (pr->tree->children->cl_obj->tp == CLERI_TP_LIST); 43 | _assert (pr->tree->children->children == NULL); 44 | cleri_parse_free(pr); 45 | } 46 | 47 | cleri_grammar_free(grammar); 48 | 49 | return test_end(); 50 | } 51 | 52 | static int test_list_all_options(void) 53 | { 54 | test_start("list (all_options)"); 55 | 56 | cleri_grammar_t * grammar; 57 | cleri_t * k_hi, * delimiter, * list; 58 | 59 | k_hi = cleri_keyword(0, "hi", false); 60 | delimiter = cleri_token(0, "-"); 61 | list = cleri_list(0, k_hi, delimiter, 1, 3, true); 62 | grammar = cleri_grammar(list, NULL); 63 | 64 | // assert statements 65 | _assert (list->via.list->min == 1); 66 | _assert (list->via.list->max == 3); 67 | _assert (list->via.list->opt_closing == true); 68 | _assert_is_valid (grammar, "hi - hi - hi"); 69 | _assert_is_valid (grammar, "hi-hi-hi-"); 70 | _assert_is_valid (grammar, "hi-"); 71 | _assert_is_valid (grammar, "hi"); 72 | _assert_is_not_valid (grammar, ""); 73 | _assert_is_not_valid (grammar, "-"); 74 | _assert_is_not_valid (grammar, "hi-hi-hi-hi"); 75 | _assert_parse_str ( 76 | grammar, 77 | "hi-hi-hi-hi-hi", 78 | "error at line 1, position 9, " 79 | "unexpected `hi`, expecting: end_of_statement", 80 | NULL); 81 | _assert_parse_str ( 82 | grammar, 83 | "hi.", 84 | "error at line 1, position 2" 85 | ", unexpected character `.`, expecting: - or end_of_statement", 86 | NULL); 87 | _assert_parse_str ( 88 | grammar, 89 | "", 90 | "error at line 1, position 0, expecting: hi", 91 | NULL); 92 | _assert_parse_str2 ( 93 | grammar, 94 | "hi-hi-hi-hi-hi", 95 | "error at line 1, position 9, unexpected `hi`", 96 | NULL); 97 | _assert_parse_str2 ( 98 | grammar, 99 | "hi.", 100 | "error at line 1, position 2, unexpected character `.`", 101 | NULL); 102 | _assert_parse_str2 ( 103 | grammar, 104 | "", 105 | "error at line 1, position 0", 106 | NULL); 107 | cleri_grammar_free(grammar); 108 | 109 | return test_end(); 110 | } 111 | 112 | static int test_list_vs_single(void) 113 | { 114 | test_start("list (vs_single)"); 115 | 116 | cleri_grammar_t * grammar; 117 | cleri_t * k_hi, * delimiter, * list, * choice; 118 | 119 | k_hi = cleri_keyword(0, "hi", false); 120 | delimiter = cleri_token(0, ","); 121 | list = cleri_list(0, k_hi, delimiter, 1, 0, false); 122 | choice = cleri_choice(0, true, 2, k_hi, list); 123 | 124 | grammar = cleri_grammar(choice, NULL); 125 | 126 | // assert statements 127 | _assert_is_valid (grammar, "hi, hi, hi"); 128 | _assert_parse_str ( 129 | grammar, 130 | "hi, hello", 131 | "error at line 1, position 4, unexpected `hello`, expecting: hi", 132 | NULL); 133 | _assert_parse_str ( 134 | grammar, 135 | "hello", 136 | "error at line 1, position 0, unexpected `hello`, expecting: hi", 137 | NULL); 138 | _assert_parse_str2 ( 139 | grammar, 140 | "hi, hello", 141 | "error at line 1, position 4, unexpected `hello`", 142 | NULL); 143 | _assert_parse_str2 ( 144 | grammar, 145 | "hello", 146 | "error at line 1, position 0, unexpected `hello`", 147 | NULL); 148 | cleri_grammar_free(grammar); 149 | 150 | return test_end(); 151 | } 152 | 153 | 154 | 155 | 156 | int main() 157 | { 158 | return ( 159 | test_list() || 160 | test_list_all_options() || 161 | test_list_vs_single() || 162 | 0 163 | ); 164 | } 165 | -------------------------------------------------------------------------------- /test/test_optional/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/optional.c 10 | -------------------------------------------------------------------------------- /test/test_optional/test_optional.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_optional(void) 6 | { 7 | test_start("optional"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * optional; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | optional = cleri_optional(0, k_hi); 14 | grammar = cleri_grammar(optional, NULL); 15 | 16 | // assert statements 17 | _assert_is_valid (grammar, "hi"); 18 | _assert_is_valid (grammar, ""); 19 | _assert_is_not_valid(grammar, "hello"); 20 | 21 | _assert_parse_str ( 22 | grammar, 23 | "hello", 24 | "error at line 1, position 0, " 25 | "unexpected `hello`, expecting: hi or end_of_statement", 26 | NULL); 27 | 28 | _assert_parse_str ( 29 | grammar, 30 | "hi hi", 31 | "error at line 1, position 2, expecting: end_of_statement", 32 | NULL); 33 | _assert_parse_str2 ( 34 | grammar, 35 | "hello", 36 | "error at line 1, position 0, unexpected `hello`", 37 | NULL); 38 | 39 | _assert_parse_str2 ( 40 | grammar, 41 | "hi hi", 42 | "error at line 1, position 2", 43 | NULL); 44 | cleri_grammar_free(grammar); 45 | 46 | return test_end(); 47 | } 48 | 49 | int main() 50 | { 51 | return ( 52 | test_optional() || 53 | 0 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /test/test_prio/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/token.c 10 | ../src/sequence.c 11 | ../src/this.c 12 | ../src/rule.c 13 | ../src/prio.c 14 | -------------------------------------------------------------------------------- /test/test_prio/test_prio.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_prio(void) 6 | { 7 | test_start("prio"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * k_bye, * k_and, * k_or, * t_open, * t_close, * prio, * t_comma, * k_if; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | k_bye = cleri_keyword(0, "bye", false); 14 | k_and = cleri_keyword(0, "and", false); 15 | k_or = cleri_keyword(0, "or", false); 16 | t_open = cleri_token(0, "("); 17 | t_close = cleri_token(0, ")"); 18 | t_comma = cleri_token(0, ","); 19 | k_if = cleri_keyword(0, "if", false); 20 | 21 | prio = cleri_prio( 22 | 0, 23 | 7, 24 | k_hi, 25 | k_bye, 26 | cleri_sequence(0, 5, k_if, t_open, CLERI_THIS, t_close, CLERI_THIS), 27 | cleri_sequence(0, 7, k_if, t_open, CLERI_THIS, t_comma, k_bye, t_close, CLERI_THIS), 28 | cleri_sequence(0, 3, t_open, CLERI_THIS, t_close), 29 | cleri_sequence(0, 3, CLERI_THIS, k_and, CLERI_THIS), 30 | cleri_sequence(0, 3, CLERI_THIS, k_or, CLERI_THIS)); 31 | 32 | grammar = cleri_grammar(prio, NULL); 33 | 34 | // assert statements 35 | _assert_is_valid(grammar, "hi"); 36 | _assert_is_valid(grammar, "(bye)"); 37 | _assert_is_valid(grammar, "(hi and bye)"); 38 | _assert_is_valid(grammar, "(hi or hi) and (hi or hi)"); 39 | _assert_is_valid(grammar, "(hi or (hi and bye))"); 40 | _assert_is_not_valid(grammar, ""); 41 | _assert_is_not_valid(grammar, "(hi"); 42 | _assert_is_not_valid(grammar, "()"); 43 | _assert_is_not_valid(grammar, "(hi or hi) and"); 44 | { 45 | cleri_parse_t * pr = cleri_parse( 46 | grammar, 47 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 48 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 49 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 50 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 51 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 52 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 53 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 54 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 55 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 56 | "(((((((((((((((((((((((((((((((((((((((((((((((((((hi" 57 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 58 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 59 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 60 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 61 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 62 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 63 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 64 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 65 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 66 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 67 | ); 68 | _assert (pr == NULL); /* max recursion depth */ 69 | 70 | } 71 | 72 | _assert_parse_str ( 73 | grammar, 74 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 75 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 76 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 77 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 78 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 79 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 80 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 81 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 82 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 83 | "(((((((((((((((((((((((((((((((((((((((((((((((((((hi" 84 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 85 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 86 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 87 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 88 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 89 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 90 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 91 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 92 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 93 | ")))))))))))))))))))))))))))))))))))))))))))))))))))", 94 | "no parse result, a possible reason might be that the maximum " 95 | "recursion depth of 500 has been reached", 96 | NULL); 97 | _assert_parse_str2 ( 98 | grammar, 99 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 100 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 101 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 102 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 103 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 104 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 105 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 106 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 107 | "(((((((((((((((((((((((((((((((((((((((((((((((((((" 108 | "(((((((((((((((((((((((((((((((((((((((((((((((((((hi" 109 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 110 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 111 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 112 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 113 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 114 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 115 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 116 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 117 | ")))))))))))))))))))))))))))))))))))))))))))))))))))" 118 | ")))))))))))))))))))))))))))))))))))))))))))))))))))", 119 | "no parse result, a possible reason might be that the maximum " 120 | "recursion depth of 500 has been reached", 121 | NULL); 122 | 123 | { 124 | cleri_parse_t * pr; 125 | cleri_node_t * node; 126 | 127 | pr = cleri_parse2( 128 | grammar, 129 | "if(hi) bye", 130 | CLERI_FLAG_EXPECTING_DISABLED| 131 | CLERI_FLAG_EXCLUDE_OPTIONAL| 132 | CLERI_FLAG_EXCLUDE_FM_CHOICE| 133 | CLERI_FLAG_EXCLUDE_RULE_THIS 134 | ); 135 | _assert (pr->is_valid); 136 | _assert (pr->tree->children->children->children->next->next->next); 137 | node = pr->tree->children->children->children->next->next->next; 138 | _assert (*node->str == ')'); 139 | _assert (node->len == 1); 140 | cleri_parse_free(pr); 141 | 142 | pr = cleri_parse2( 143 | grammar, 144 | "if(hi, bye) bye", 145 | CLERI_FLAG_EXPECTING_DISABLED| 146 | CLERI_FLAG_EXCLUDE_OPTIONAL| 147 | CLERI_FLAG_EXCLUDE_FM_CHOICE| 148 | CLERI_FLAG_EXCLUDE_RULE_THIS 149 | ); 150 | _assert (pr->is_valid); 151 | _assert (pr->tree->children->children->children->next->next->next); 152 | node = pr->tree->children->children->children->next->next->next; 153 | _assert (*node->str == ','); 154 | _assert (node->len == 1); 155 | cleri_parse_free(pr); 156 | } 157 | 158 | cleri_grammar_free(grammar); 159 | 160 | return test_end(); 161 | } 162 | 163 | int main() 164 | { 165 | return ( 166 | test_prio() || 167 | 0 168 | ); 169 | } 170 | -------------------------------------------------------------------------------- /test/test_ref/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/ref.c 10 | -------------------------------------------------------------------------------- /test/test_ref/test_ref.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_ref(void) 6 | { 7 | test_start("ref"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * ref; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | ref = cleri_ref(); 14 | grammar = cleri_grammar(ref, NULL); 15 | 16 | /* now set the reference */ 17 | cleri_ref_set(ref, k_hi); 18 | 19 | _assert_is_valid (grammar, "hi"); 20 | _assert_is_not_valid (grammar, ""); 21 | _assert_parse_str ( 22 | grammar, 23 | "ha", 24 | "error at line 1, position 0, unexpected `ha`, expecting: hi", 25 | NULL); 26 | _assert_parse_str2 ( 27 | grammar, 28 | "ha", 29 | "error at line 1, position 0, unexpected `ha`", 30 | NULL); 31 | cleri_grammar_free(grammar); 32 | 33 | return test_end(); 34 | } 35 | 36 | int main() 37 | { 38 | return ( 39 | test_ref() || 40 | 0 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /test/test_regex/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/regex.c 9 | -------------------------------------------------------------------------------- /test/test_regex/test_regex.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | #define id 5 5 | 6 | static const char * translate(cleri_t * o) 7 | { 8 | switch (o->gid) 9 | { 10 | case id: 11 | return "single_quoted_string"; 12 | } 13 | return NULL; 14 | } 15 | 16 | 17 | static int test_regex(void) 18 | { 19 | test_start("regex"); 20 | 21 | cleri_grammar_t * grammar; 22 | cleri_t * regex; 23 | 24 | regex = cleri_regex(id, "^(?:\'(?:[^\']*)\')+"); 25 | _assert (regex); 26 | grammar = cleri_grammar(regex, NULL); 27 | 28 | _assert (grammar); 29 | _assert (regex->gid == id); 30 | _assert_is_valid (grammar, "'hi'"); 31 | _assert_is_valid (grammar, "'this is ''SiriDB'''"); 32 | _assert_is_not_valid (grammar, "'Hi !"); 33 | _assert_is_not_valid (grammar, "'hello''"); 34 | _assert_is_not_valid (grammar, ""); 35 | _assert_parse_str ( 36 | grammar, 37 | "\"double quoted\"", 38 | "error at line 1, position 0, " 39 | "unexpected character `\"`, expecting: single_quoted_string", 40 | translate); 41 | _assert_parse_str2 ( 42 | grammar, 43 | "\"double quoted\"", 44 | "error at line 1, position 0, " 45 | "unexpected character `\"`", 46 | translate); 47 | cleri_grammar_free(grammar); 48 | 49 | return test_end(); 50 | } 51 | 52 | int main() 53 | { 54 | return ( 55 | test_regex() || 56 | 0 57 | ); 58 | } 59 | -------------------------------------------------------------------------------- /test/test_repeat/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/repeat.c 10 | -------------------------------------------------------------------------------- /test/test_repeat/test_repeat.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static const char * translate(cleri_t * o __attribute__((unused))) 6 | { 7 | /* this should ignore expecting: ... */ 8 | return ""; 9 | } 10 | 11 | static int test_repeat(void) 12 | { 13 | test_start("repeat"); 14 | 15 | cleri_grammar_t * grammar; 16 | cleri_t * k_hi, * repeat; 17 | 18 | k_hi = cleri_keyword(0, "hi", false); 19 | repeat = cleri_repeat(0, k_hi, 0, 0); 20 | grammar = cleri_grammar(repeat, NULL); 21 | 22 | // assert statements 23 | _assert (repeat->via.repeat->min == 0); 24 | _assert (repeat->via.repeat->max == 0); 25 | _assert_is_valid (grammar, "hi hi hi"); 26 | _assert_is_valid (grammar, "hi"); 27 | _assert_is_valid (grammar, ""); 28 | _assert_is_not_valid (grammar, "hihi"); 29 | _assert_is_not_valid (grammar, "ha"); 30 | _assert_parse_str ( 31 | grammar, 32 | "hi.", 33 | "error at line 1, position 2, " 34 | "unexpected character `.`, expecting: hi or end_of_statement", 35 | NULL); 36 | _assert_parse_str ( 37 | grammar, 38 | "hi.", 39 | "error at line 1, position 2, " 40 | "unexpected character `.`", 41 | &translate); 42 | _assert_parse_str2 ( 43 | grammar, 44 | "hi.", 45 | "error at line 1, position 2, " 46 | "unexpected character `.`", 47 | NULL); 48 | _assert_parse_str2 ( 49 | grammar, 50 | "hi.", 51 | "error at line 1, position 2, " 52 | "unexpected character `.`", 53 | &translate); 54 | cleri_grammar_free(grammar); 55 | 56 | return test_end(); 57 | } 58 | 59 | static int test_repeat_all_options(void) 60 | { 61 | test_start("repeat (all_options)"); 62 | 63 | cleri_grammar_t * grammar; 64 | cleri_t * k_hi, * repeat; 65 | 66 | k_hi = cleri_keyword(0, "hi", false); 67 | repeat = cleri_repeat(0, k_hi, 1, 3); 68 | grammar = cleri_grammar(repeat, NULL); 69 | 70 | // assert statements 71 | _assert (repeat->via.repeat->min == 1); 72 | _assert (repeat->via.repeat->max == 3); 73 | _assert_is_valid (grammar, "hi hi hi"); 74 | _assert_is_valid (grammar, "hi"); 75 | _assert_is_not_valid (grammar, ""); 76 | _assert_is_not_valid (grammar, "hi hi hi hi"); 77 | _assert_parse_str ( 78 | grammar, 79 | "hi hi hi hi hi", 80 | "error at line 1, position 8, expecting: end_of_statement", 81 | NULL); 82 | _assert_parse_str ( 83 | grammar, 84 | "hi.", 85 | "error at line 1, position 2, " 86 | "unexpected character `.`, expecting: hi or end_of_statement", 87 | NULL); 88 | _assert_parse_str ( 89 | grammar, 90 | "", 91 | "error at line 1, position 0, expecting: hi", 92 | NULL); 93 | _assert_parse_str2 ( 94 | grammar, 95 | "hi hi hi hi hi", 96 | "error at line 1, position 8", 97 | NULL); 98 | _assert_parse_str2 ( 99 | grammar, 100 | "hi.", 101 | "error at line 1, position 2, " 102 | "unexpected character `.`", 103 | NULL); 104 | _assert_parse_str2 ( 105 | grammar, 106 | "", 107 | "error at line 1, position 0", 108 | NULL); 109 | cleri_grammar_free(grammar); 110 | 111 | return test_end(); 112 | } 113 | 114 | int main() 115 | { 116 | return ( 117 | test_repeat() || 118 | test_repeat_all_options() || 119 | 0 120 | ); 121 | } 122 | -------------------------------------------------------------------------------- /test/test_sequence/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/keyword.c 9 | ../src/sequence.c 10 | -------------------------------------------------------------------------------- /test/test_sequence/test_sequence.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_sequence(void) 6 | { 7 | test_start("sequence"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * k_hi, * k_iris, * seq; 11 | 12 | k_hi = cleri_keyword(0, "hi", false); 13 | k_iris = cleri_keyword(0, "iris", false); 14 | seq = cleri_sequence(0, 2, k_hi, k_iris); 15 | grammar = cleri_grammar(seq, NULL); 16 | 17 | _assert_is_valid (grammar, "hi iris"); 18 | _assert_is_not_valid (grammar, "hi sasha"); 19 | _assert_parse_str ( 20 | grammar, 21 | "hi sasha", 22 | "error at line 1, position 3, " 23 | "unexpected `sasha`, expecting: iris", 24 | NULL); 25 | _assert_parse_str2 ( 26 | grammar, 27 | "hi sasha", 28 | "error at line 1, position 3, " 29 | "unexpected `sasha`", 30 | NULL); 31 | cleri_grammar_free(grammar); 32 | 33 | return test_end(); 34 | } 35 | 36 | int main() 37 | { 38 | return ( 39 | test_sequence() || 40 | 0 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /test/test_thingsdb_lang/sources: -------------------------------------------------------------------------------- 1 | ../src/choice.c 2 | ../src/cleri.c 3 | ../src/dup.c 4 | ../src/expecting.c 5 | ../src/grammar.c 6 | ../src/keyword.c 7 | ../src/kwcache.c 8 | ../src/list.c 9 | ../src/node.c 10 | ../src/olist.c 11 | ../src/optional.c 12 | ../src/parse.c 13 | ../src/prio.c 14 | ../src/ref.c 15 | ../src/regex.c 16 | ../src/repeat.c 17 | ../src/rule.c 18 | ../src/sequence.c 19 | ../src/this.c 20 | ../src/token.c 21 | ../src/tokens.c 22 | -------------------------------------------------------------------------------- /test/test_thingsdb_lang/test_thingsdb_lang.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | enum cleri_grammar_ids { 5 | CLERI_NONE, // used for objects with no name 6 | CLERI_GID_ARRAY, 7 | CLERI_GID_ASSIGN, 8 | CLERI_GID_BLOCK, 9 | CLERI_GID_CHAIN, 10 | CLERI_GID_CLOSURE, 11 | CLERI_GID_COMMENTS, 12 | CLERI_GID_END_STATEMENT, 13 | CLERI_GID_ENUM_, 14 | CLERI_GID_EXPRESSION, 15 | CLERI_GID_FOR_STATEMENT, 16 | CLERI_GID_FUNCTION, 17 | CLERI_GID_IF_STATEMENT, 18 | CLERI_GID_INDEX, 19 | CLERI_GID_INSTANCE, 20 | CLERI_GID_K_BREAK, 21 | CLERI_GID_K_CONTINUE, 22 | CLERI_GID_K_ELSE, 23 | CLERI_GID_K_FOR, 24 | CLERI_GID_K_IF, 25 | CLERI_GID_K_IN, 26 | CLERI_GID_K_RETURN, 27 | CLERI_GID_NAME, 28 | CLERI_GID_NAME_OPT_MORE, 29 | CLERI_GID_OPERATIONS, 30 | CLERI_GID_OPR0_MUL_DIV_MOD, 31 | CLERI_GID_OPR1_ADD_SUB, 32 | CLERI_GID_OPR2_BITWISE_AND, 33 | CLERI_GID_OPR3_BITWISE_XOR, 34 | CLERI_GID_OPR4_BITWISE_OR, 35 | CLERI_GID_OPR5_COMPARE, 36 | CLERI_GID_OPR6_CMP_AND, 37 | CLERI_GID_OPR7_CMP_OR, 38 | CLERI_GID_OPR8_TERNARY, 39 | CLERI_GID_PARENTHESIS, 40 | CLERI_GID_RETURN_STATEMENT, 41 | CLERI_GID_SLICE, 42 | CLERI_GID_START, 43 | CLERI_GID_STATEMENT, 44 | CLERI_GID_STATEMENTS, 45 | CLERI_GID_TEMPLATE, 46 | CLERI_GID_THING, 47 | CLERI_GID_T_FALSE, 48 | CLERI_GID_T_FLOAT, 49 | CLERI_GID_T_INT, 50 | CLERI_GID_T_NIL, 51 | CLERI_GID_T_REGEX, 52 | CLERI_GID_T_STRING, 53 | CLERI_GID_T_TRUE, 54 | CLERI_GID_VAR, 55 | CLERI_GID_VAR_OPT_MORE, 56 | CLERI_GID_X_ARRAY, 57 | CLERI_GID_X_ASSIGN, 58 | CLERI_GID_X_BLOCK, 59 | CLERI_GID_X_CHAIN, 60 | CLERI_GID_X_CLOSURE, 61 | CLERI_GID_X_FUNCTION, 62 | CLERI_GID_X_INDEX, 63 | CLERI_GID_X_PARENTHESIS, 64 | CLERI_GID_X_PREOPR, 65 | CLERI_GID_X_TERNARY, 66 | CLERI_GID_X_THING, 67 | CLERI_END // can be used to get the enum length 68 | }; 69 | 70 | #define CLERI_CASE_SENSITIVE 0 71 | #define CLERI_CASE_INSENSITIVE 1 72 | 73 | #define CLERI_FIRST_MATCH 0 74 | #define CLERI_MOST_GREEDY 1 75 | 76 | cleri_grammar_t * compile_langdef(void) 77 | { 78 | cleri_t * x_array = cleri_token(CLERI_GID_X_ARRAY, "["); 79 | cleri_t * x_assign = cleri_tokens(CLERI_GID_X_ASSIGN, "+= -= *= /= %= &= ^= |= ="); 80 | cleri_t * x_block = cleri_token(CLERI_GID_X_BLOCK, "{"); 81 | cleri_t * x_chain = cleri_token(CLERI_GID_X_CHAIN, "."); 82 | cleri_t * x_closure = cleri_token(CLERI_GID_X_CLOSURE, "|"); 83 | cleri_t * x_function = cleri_token(CLERI_GID_X_FUNCTION, "("); 84 | cleri_t * x_index = cleri_token(CLERI_GID_X_INDEX, "["); 85 | cleri_t * x_parenthesis = cleri_token(CLERI_GID_X_PARENTHESIS, "("); 86 | cleri_t * x_preopr = cleri_regex(CLERI_GID_X_PREOPR, "^(\\s*!|\\s*[\\-+](?=[^0-9]))*"); 87 | cleri_t * x_ternary = cleri_token(CLERI_GID_X_TERNARY, "?"); 88 | cleri_t * x_thing = cleri_token(CLERI_GID_X_THING, "{"); 89 | cleri_t * template = cleri_sequence( 90 | CLERI_GID_TEMPLATE, 91 | 3, 92 | cleri_token(CLERI_NONE, "`"), 93 | cleri_repeat(CLERI_NONE, cleri_choice( 94 | CLERI_NONE, 95 | CLERI_FIRST_MATCH, 96 | 2, 97 | cleri_regex(CLERI_NONE, "^([^`{}]|``|{{|}})+"), 98 | cleri_sequence( 99 | CLERI_NONE, 100 | 3, 101 | cleri_token(CLERI_NONE, "{"), 102 | CLERI_THIS, 103 | cleri_token(CLERI_NONE, "}") 104 | ) 105 | ), 0, 0), 106 | cleri_token(CLERI_NONE, "`") 107 | ); 108 | cleri_t * t_false = cleri_keyword(CLERI_GID_T_FALSE, "false", CLERI_CASE_SENSITIVE); 109 | cleri_t * t_float = cleri_regex(CLERI_GID_T_FLOAT, "^[-+]?(inf|nan|[0-9]*\\.[0-9]+(e[+-][0-9]+)?)(?![0-9A-Za-z_])"); 110 | cleri_t * t_int = cleri_regex(CLERI_GID_T_INT, "^[-+]?((0b[01]+)|(0o[0-8]+)|(0x[0-9a-fA-F]+)|([0-9]+))(?![0-9A-Za-z_])"); 111 | cleri_t * t_nil = cleri_keyword(CLERI_GID_T_NIL, "nil", CLERI_CASE_SENSITIVE); 112 | cleri_t * t_regex = cleri_regex(CLERI_GID_T_REGEX, "^/((?:.(?!(?= < >"); 180 | cleri_t * opr6_cmp_and = cleri_token(CLERI_GID_OPR6_CMP_AND, "&&"); 181 | cleri_t * opr7_cmp_or = cleri_token(CLERI_GID_OPR7_CMP_OR, "||"); 182 | cleri_t * opr8_ternary = cleri_sequence( 183 | CLERI_GID_OPR8_TERNARY, 184 | 3, 185 | x_ternary, 186 | CLERI_THIS, 187 | cleri_token(CLERI_NONE, ":") 188 | ); 189 | cleri_t * operations = cleri_sequence( 190 | CLERI_GID_OPERATIONS, 191 | 3, 192 | CLERI_THIS, 193 | cleri_choice( 194 | CLERI_NONE, 195 | CLERI_FIRST_MATCH, 196 | 9, 197 | opr8_ternary, 198 | opr7_cmp_or, 199 | opr6_cmp_and, 200 | opr5_compare, 201 | opr4_bitwise_or, 202 | opr3_bitwise_xor, 203 | opr2_bitwise_and, 204 | opr1_add_sub, 205 | opr0_mul_div_mod 206 | ), 207 | CLERI_THIS 208 | ); 209 | cleri_t * assign = cleri_sequence( 210 | CLERI_GID_ASSIGN, 211 | 2, 212 | x_assign, 213 | CLERI_THIS 214 | ); 215 | cleri_t * name_opt_more = cleri_sequence( 216 | CLERI_GID_NAME_OPT_MORE, 217 | 2, 218 | name, 219 | cleri_optional(CLERI_NONE, cleri_choice( 220 | CLERI_NONE, 221 | CLERI_FIRST_MATCH, 222 | 2, 223 | function, 224 | assign 225 | )) 226 | ); 227 | cleri_t * var_opt_more = cleri_sequence( 228 | CLERI_GID_VAR_OPT_MORE, 229 | 2, 230 | var, 231 | cleri_optional(CLERI_NONE, cleri_choice( 232 | CLERI_NONE, 233 | CLERI_FIRST_MATCH, 234 | 4, 235 | function, 236 | assign, 237 | instance, 238 | enum_ 239 | )) 240 | ); 241 | cleri_t * slice = cleri_list(CLERI_GID_SLICE, cleri_optional(CLERI_NONE, CLERI_THIS), cleri_token(CLERI_NONE, ":"), 0, 3, 0); 242 | cleri_t * index = cleri_repeat(CLERI_GID_INDEX, cleri_sequence( 243 | CLERI_NONE, 244 | 4, 245 | x_index, 246 | slice, 247 | cleri_token(CLERI_NONE, "]"), 248 | cleri_optional(CLERI_NONE, cleri_sequence( 249 | CLERI_NONE, 250 | 2, 251 | x_assign, 252 | CLERI_THIS 253 | )) 254 | ), 0, 0); 255 | cleri_t * end_statement = cleri_regex(CLERI_GID_END_STATEMENT, "^((;|((?s)\\/\\/.*?(\\r?\\n|$))|((?s)\\/\\*.*?\\*\\/))\\s*)*"); 256 | cleri_t * block = cleri_sequence( 257 | CLERI_GID_BLOCK, 258 | 4, 259 | x_block, 260 | comments, 261 | cleri_list(CLERI_NONE, CLERI_THIS, end_statement, 1, 0, 1), 262 | cleri_token(CLERI_NONE, "}") 263 | ); 264 | cleri_t * parenthesis = cleri_sequence( 265 | CLERI_GID_PARENTHESIS, 266 | 3, 267 | x_parenthesis, 268 | CLERI_THIS, 269 | cleri_token(CLERI_NONE, ")") 270 | ); 271 | cleri_t * k_if = cleri_keyword(CLERI_GID_K_IF, "if", CLERI_CASE_SENSITIVE); 272 | cleri_t * k_else = cleri_keyword(CLERI_GID_K_ELSE, "else", CLERI_CASE_SENSITIVE); 273 | cleri_t * k_return = cleri_keyword(CLERI_GID_K_RETURN, "return", CLERI_CASE_SENSITIVE); 274 | cleri_t * k_for = cleri_keyword(CLERI_GID_K_FOR, "for", CLERI_CASE_SENSITIVE); 275 | cleri_t * k_in = cleri_keyword(CLERI_GID_K_IN, "in", CLERI_CASE_SENSITIVE); 276 | cleri_t * k_continue = cleri_keyword(CLERI_GID_K_CONTINUE, "continue", CLERI_CASE_SENSITIVE); 277 | cleri_t * k_break = cleri_keyword(CLERI_GID_K_BREAK, "break", CLERI_CASE_SENSITIVE); 278 | cleri_t * if_statement = cleri_sequence( 279 | CLERI_GID_IF_STATEMENT, 280 | 6, 281 | k_if, 282 | cleri_token(CLERI_NONE, "("), 283 | CLERI_THIS, 284 | cleri_token(CLERI_NONE, ")"), 285 | CLERI_THIS, 286 | cleri_optional(CLERI_NONE, cleri_sequence( 287 | CLERI_NONE, 288 | 2, 289 | k_else, 290 | CLERI_THIS 291 | )) 292 | ); 293 | cleri_t * return_statement = cleri_sequence( 294 | CLERI_GID_RETURN_STATEMENT, 295 | 3, 296 | k_return, 297 | CLERI_THIS, 298 | cleri_optional(CLERI_NONE, cleri_sequence( 299 | CLERI_NONE, 300 | 2, 301 | cleri_token(CLERI_NONE, ","), 302 | CLERI_THIS 303 | )) 304 | ); 305 | cleri_t * for_statement = cleri_sequence( 306 | CLERI_GID_FOR_STATEMENT, 307 | 7, 308 | k_for, 309 | cleri_token(CLERI_NONE, "("), 310 | cleri_list(CLERI_NONE, var, cleri_token(CLERI_NONE, ","), 1, 0, 0), 311 | k_in, 312 | CLERI_THIS, 313 | cleri_token(CLERI_NONE, ")"), 314 | CLERI_THIS 315 | ); 316 | cleri_t * expression = cleri_sequence( 317 | CLERI_GID_EXPRESSION, 318 | 4, 319 | x_preopr, 320 | cleri_choice( 321 | CLERI_NONE, 322 | CLERI_FIRST_MATCH, 323 | 13, 324 | chain, 325 | t_false, 326 | t_nil, 327 | t_true, 328 | t_float, 329 | t_int, 330 | t_string, 331 | t_regex, 332 | template, 333 | var_opt_more, 334 | thing, 335 | array, 336 | parenthesis 337 | ), 338 | index, 339 | cleri_optional(CLERI_NONE, chain) 340 | ); 341 | cleri_t * statement = cleri_prio( 342 | CLERI_GID_STATEMENT, 343 | 4, 344 | k_continue, 345 | k_break, 346 | cleri_choice( 347 | CLERI_NONE, 348 | CLERI_FIRST_MATCH, 349 | 6, 350 | if_statement, 351 | return_statement, 352 | for_statement, 353 | closure, 354 | expression, 355 | block 356 | ), 357 | operations 358 | ); 359 | cleri_t * statements = cleri_list(CLERI_GID_STATEMENTS, statement, end_statement, 0, 0, 1); 360 | cleri_t * START = cleri_sequence( 361 | CLERI_GID_START, 362 | 2, 363 | comments, 364 | statements 365 | ); 366 | cleri_ref_set(chain, cleri_sequence( 367 | CLERI_GID_CHAIN, 368 | 4, 369 | x_chain, 370 | name_opt_more, 371 | index, 372 | cleri_optional(CLERI_NONE, chain) 373 | )); 374 | 375 | cleri_grammar_t * grammar = cleri_grammar(START, "^[A-Za-z_][0-9A-Za-z_]{0,254}(?![0-9A-Za-z_])"); 376 | 377 | return grammar; 378 | } 379 | 380 | static char * query = \ 381 | "for (x in range(100)) { \n" 382 | " user = .users.find(|user| user.name.upper() == 'test');\n" 383 | " \n" 384 | " if (user) { \n" 385 | " return 'user found!'; \n" 386 | " } else { \n" 387 | " wse({ \n" 388 | " user = User{name: 'test'}; \n" 389 | " .users.push(user); \n" 390 | " }); \n" 391 | " .room.emit('new-user', user); \n" 392 | " } \n" 393 | " \n" 394 | " .channel = Channel(id); \n" 395 | " .channel.user = user; \n" 396 | " .users.reduce(|arr, user| { \n" 397 | " user = user.filter(|prop| prop == 'name'); \n" 398 | " arr.push(user); \n" 399 | " arr; \n" 400 | " }, []); \n" 401 | "} \n"; 402 | 403 | static int test_thingsdb_lang(void) 404 | { 405 | test_start("thingsdb (language)"); 406 | 407 | cleri_grammar_t * grammar = compile_langdef(); 408 | _assert (grammar); 409 | 410 | int i, flags; 411 | char buf[262144]; 412 | char * str = buf; 413 | size_t query_len = strlen(query); 414 | for (i = 0; i < 20; i++) // max 200 415 | { 416 | memcpy(str, query, query_len); 417 | str += query_len; 418 | } 419 | *str = '\0'; 420 | 421 | _assert_is_valid (grammar, "x = 1"); 422 | _assert_is_valid (grammar, "||1?2:3"); 423 | _assert_is_valid (grammar, "||nil"); 424 | _assert_is_valid (grammar, "4 + 5;"); 425 | _assert_is_not_valid (grammar, "||1?2"); 426 | 427 | flags = ( 428 | CLERI_FLAG_EXPECTING_DISABLED| 429 | CLERI_FLAG_EXCLUDE_OPTIONAL| 430 | CLERI_FLAG_EXCLUDE_FM_CHOICE| 431 | CLERI_FLAG_EXCLUDE_RULE_THIS 432 | ); 433 | 434 | _assert_is_valid_flags (grammar, buf, flags); 435 | 436 | cleri_grammar_free(grammar); 437 | 438 | return test_end(); 439 | } 440 | 441 | int main() 442 | { 443 | return ( 444 | test_thingsdb_lang() || 445 | 0 446 | ); 447 | } -------------------------------------------------------------------------------- /test/test_token/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/token.c 9 | -------------------------------------------------------------------------------- /test/test_token/test_token.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_token(void) 6 | { 7 | test_start("token"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * dot; 11 | 12 | dot = cleri_token(0, "."); 13 | grammar = cleri_grammar(dot, NULL); 14 | 15 | // assert statements 16 | _assert_is_valid(grammar, "."); 17 | _assert_is_not_valid(grammar, ".."); 18 | _assert_is_not_valid(grammar, ""); 19 | _assert_parse_str ( 20 | grammar, 21 | "", 22 | "error at line 1, position 0, expecting: .", 23 | NULL); 24 | _assert_parse_str2 ( 25 | grammar, 26 | "", 27 | "error at line 1, position 0", 28 | NULL); 29 | 30 | cleri_grammar_free(grammar); 31 | 32 | return test_end(); 33 | } 34 | 35 | static int test_token_multi_char(void) 36 | { 37 | test_start("token (multi_char)"); 38 | 39 | cleri_grammar_t * grammar; 40 | cleri_t * not; 41 | 42 | not = cleri_token(0, "!="); 43 | grammar = cleri_grammar(not, NULL); 44 | 45 | // assert statements 46 | _assert_is_valid(grammar, " != "); 47 | _assert_is_not_valid(grammar, "!"); 48 | _assert_is_not_valid(grammar, ""); 49 | _assert_parse_str ( 50 | grammar, 51 | "", 52 | "error at line 1, position 0, expecting: !=", 53 | NULL); 54 | _assert_parse_str2 ( 55 | grammar, 56 | "", 57 | "error at line 1, position 0", 58 | NULL); 59 | cleri_grammar_free(grammar); 60 | 61 | return test_end(); 62 | } 63 | 64 | int main() 65 | { 66 | return ( 67 | test_token() || 68 | test_token_multi_char() || 69 | 0 70 | ); 71 | } 72 | -------------------------------------------------------------------------------- /test/test_tokens/sources: -------------------------------------------------------------------------------- 1 | ../src/cleri.c 2 | ../src/expecting.c 3 | ../src/grammar.c 4 | ../src/kwcache.c 5 | ../src/node.c 6 | ../src/olist.c 7 | ../src/parse.c 8 | ../src/tokens.c 9 | -------------------------------------------------------------------------------- /test/test_tokens/test_tokens.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include "../helpers.h" 3 | 4 | 5 | static int test_tokens(void) 6 | { 7 | test_start("tokens"); 8 | 9 | cleri_grammar_t * grammar; 10 | cleri_t * tokens; 11 | /* tokes will be ordered on length */ 12 | const char * spaced = "== != >= <= > <"; 13 | 14 | tokens = cleri_tokens(0, "== > != < >= <= "); 15 | grammar = cleri_grammar(tokens, NULL); 16 | 17 | // assert statements 18 | _assert (strcmp(tokens->via.tokens->spaced, spaced) == 0); 19 | _assert_is_valid (grammar, "=="); 20 | _assert_is_valid (grammar, "<="); 21 | _assert_is_valid (grammar, ">"); 22 | _assert_is_not_valid (grammar, ""); 23 | _assert_is_not_valid (grammar, "="); 24 | _assert_parse_str ( 25 | grammar, 26 | "", 27 | "error at line 1, position 0, expecting: == != >= <= > <", 28 | NULL); 29 | _assert_parse_str2 ( 30 | grammar, 31 | "", 32 | "error at line 1, position 0", 33 | NULL); 34 | cleri_grammar_free(grammar); 35 | 36 | return test_end(); 37 | } 38 | 39 | int main() 40 | { 41 | return ( 42 | test_tokens() || 43 | 0 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /test/test_version/sources: -------------------------------------------------------------------------------- 1 | ../src/version.c -------------------------------------------------------------------------------- /test/test_version/test_version.c: -------------------------------------------------------------------------------- 1 | #include "../test.h" 2 | #include 3 | 4 | static int test_version(void) 5 | { 6 | test_start("version"); 7 | 8 | _assert ( strcmp(cleri_version(), "1.0.2") == 0 ); 9 | 10 | return test_end(); 11 | } 12 | 13 | int main() 14 | { 15 | return ( 16 | test_version() || 17 | 0 18 | ); 19 | } 20 | --------------------------------------------------------------------------------