Compare commits

...

15 Commits

Author SHA1 Message Date
xenia 7652bf32c4 no uchar 2026-07-21 13:54:12 -04:00
xenia eb49898a0a create package.nix 2026-07-21 13:29:39 -04:00
xenia 5c43aaa6ef the dragon girls take over: part 2 2026-07-21 13:26:17 -04:00
xenia 1ea4cadd3c correct UTF-8 surrogate range check 2026-07-21 13:18:40 -04:00
Hugo Heuzard 667c8d0945 Use default flags 2026-07-21 13:17:01 -04:00
Hugo Heuzard 409fc80d40 Document longest match behavior 2026-07-21 13:13:11 -04:00
xenia 1b2ea6e119 update opam file 2026-07-21 13:12:36 -04:00
Hugo Heuzard 7606e062aa Refactoring, new type for dfa_state and dfa 2026-07-21 13:11:47 -04:00
Hugo Heuzard 50488309ed Tests: output automata using dot syntax for easier review 2026-07-21 13:11:24 -04:00
Hugo Heuzard e1467df71b Tests: add codegen tests 2026-07-21 13:10:51 -04:00
Hugo Heuzard 15df9eb34f Tests: add a %sedlex_test ppx to help write tests 2026-07-21 13:09:48 -04:00
xenia 7679a0d740 bump ocamlformat 2026-07-21 13:08:05 -04:00
xenia d0371e2226 the dragon girls take over 2026-07-21 13:04:41 -04:00
Romain Beauxis 589f26b0cc Release v3.7! 2025-10-06 16:25:12 -05:00
Romain Beauxis 7fae55f1ac
Update to Unicode 17.0.0 (#172) 2025-09-11 15:22:41 -05:00
41 changed files with 2311 additions and 2041 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

3
.github/CODEOWNERS vendored
View File

@ -1,3 +0,0 @@
# These are the default owners for everything in the repo. They will
# be requested for review when someone opens a pull request.
* @alainfrisch @Drup @pmetzger

View File

@ -1,69 +0,0 @@
name: build
on:
merge_group:
pull_request:
push:
branches:
- master
schedule:
# Prime the caches every Monday
- cron: 0 1 * * MON
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- macos-latest
- windows-latest
ocaml-compiler:
- 4.14.x
- 5.x
runs-on: ${{ matrix.os }}
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
git config --global core.ignorecase false
- name: Checkout code
uses: actions/checkout@v4
- name: Use OCaml ${{ matrix.ocaml-compiler }}
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: ${{ matrix.ocaml-compiler }}
dune-cache: ${{ matrix.os == 'ubuntu-latest' }}
- run: opam install . --with-test
- run: opam exec -- make build
- run: opam exec -- make test
- run: opam exec -- git diff --exit-code
lint-fmt:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use OCaml 4.14.x
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 4.14.x
dune-cache: true
- name: Lint fmt
uses: ocaml/setup-ocaml/lint-fmt@v3

View File

@ -1,20 +0,0 @@
name: Check changelog
on:
pull_request:
branches:
- master
types:
- labeled
- opened
- reopened
- synchronize
- unlabeled
jobs:
check-changelog:
name: Check changelog
runs-on: ubuntu-latest
steps:
- name: Check changelog
uses: tarides/changelog-check-action@v1

View File

@ -1,28 +0,0 @@
name: Doc build
on:
push:
branches:
- master
jobs:
build_doc:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup OCaml
uses: ocaml/setup-ocaml@v3
with:
ocaml-compiler: 4.14.x
- name: Pin locally
run: opam pin -y add --no-action .
- name: Install locally
run: opam install -y --with-doc sedlex
- name: Build doc
run: opam exec dune build @doc
- name: Deploy doc
uses: JamesIves/github-pages-deploy-action@4.1.4
with:
branch: gh-pages
folder: _build/default/_doc/_html

1
.gitignore vendored
View File

@ -19,3 +19,4 @@ examples/complement
examples/tokenizer examples/tokenizer
examples/subtraction examples/subtraction
_opam _opam
.direnv

View File

@ -1,4 +1,4 @@
version=0.27.0 version=0.29.0
profile = conventional profile = conventional
break-separators = after break-separators = after
space-around-lists = false space-around-lists = false

View File

@ -1,19 +0,0 @@
language: c
sudo: required
install: test -e .travis.opam.sh || wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-opam.sh
script: bash -ex .travis-opam.sh
env:
global:
- PACKAGE=sedlex
matrix:
- OCAML_VERSION=4.04
- OCAML_VERSION=4.05
- OCAML_VERSION=4.06
- OCAML_VERSION=4.07
- OCAML_VERSION=4.08
- OCAML_VERSION=4.09
- OCAML_VERSION=4.10
- OCAML_VERSION=4.11
os:
- linux
- osx

View File

@ -1,3 +1,6 @@
# 3.7 (2025-10-06)
- Update to unicode 17.0.0
# 3.6 (2025-01-05) # 3.6 (2025-01-05)
- Fixed one of the ranges implementing - Fixed one of the ranges implementing
Implement Corrigendum #1: UTF-8 Shortest Form Implement Corrigendum #1: UTF-8 Shortest Form

26
LICENSE
View File

@ -1,22 +1,6 @@
The MIT License (MIT) it's proprietary. all rights reserved. if you use this code in a way i don't like i will personally
make your car explode into hammers
Copyright 2005, 2014 by Alain Frisch and LexiFi. Portions of this code are based on "sedlex", Copyright 2005, 2014 by Alain
Frisch and LexiFi, released under the terms of the MIT license which can be
Permission is hereby granted, free of charge, to any person obtaining found in <licenses/LICENSE.sedlex>
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,27 +0,0 @@
# The package sedlex is released under the terms of an MIT-like license.
# See the attached LICENSE file.
# Copyright 2005, 2013 by Alain Frisch and LexiFi.
INSTALL_ARGS := $(if $(PREFIX),--prefix $(PREFIX),)
.PHONY: build install uninstall clean doc test all
build:
dune build @install
install:
dune install $(INSTALL_ARGS)
uninstall:
dune uninstall $(INSTALL_ARGS)
clean:
dune clean
doc:
dune build @doc
test:
dune build @runtest
all: build test doc

View File

@ -79,6 +79,19 @@ where:
Unlike ocamllex, lexers work on stream of Unicode codepoints, not Unlike ocamllex, lexers work on stream of Unicode codepoints, not
bytes. bytes.
Like ocamllex, sedlex uses **longest match** with **first rule priority**:
- The lexer always tries to match the longest possible prefix of the
input. It does so by continuing to read characters as long as some
rule can still match a longer string, while remembering the last
position at which a rule did match.
- When two or more rules match the same longest prefix (a tie), the
rule that appears first in the `match%sedlex` definition wins. For
example, given the rules `| "if" -> ...` and `| Plus ('a'..'z') -> ...`,
the input `"if"` is matched by the first rule because it is listed
first, even though the second rule also accepts `"if"`.
The actions can call functions from the Sedlexing module to extract The actions can call functions from the Sedlexing module to extract
(parts of) the matched lexeme, in the desired encoding. (parts of) the matched lexeme, in the desired encoding.

View File

@ -1,18 +1,21 @@
(lang dune 3.0) (lang dune 3.20)
(version 3.6)
(name sedlex) (version "3.7")
(source (github ocaml-community/sedlex))
(license MIT) (name noslop-sedlex)
(authors "Alain Frisch <alain.frisch@lexifi.com>" (source (uri "https://git.lain.faith/noslop/noslop-sedlex.git"))
"https://github.com/ocaml-community/sedlex/graphs/contributors") (license "LicenseRef-Proprietary")
(maintainers "Alain Frisch <alain.frisch@lexifi.com>") (authors "xenia <xenia@awoo.systems>")
(homepage "https://github.com/ocaml-community/sedlex") (maintainers "xenia <xenia@awoo.systems")
(homepage "https://git.lain.faith/noslop/noslop-sedlex.git")
(maintenance_intent "(latest)")
(documentation "https://git.lain.faith/noslop/noslop-sedlex/wiki")
(generate_opam_files true) (generate_opam_files true)
(executables_implicit_empty_intf true) (executables_implicit_empty_intf true)
(package (package
(name sedlex) (name noslop-sedlex)
(synopsis "An OCaml lexer generator for Unicode") (synopsis "An OCaml lexer generator for Unicode")
(description "sedlex is a lexer generator for OCaml. It is similar to ocamllex, but supports (description "sedlex is a lexer generator for OCaml. It is similar to ocamllex, but supports
Unicode. Unlike ocamllex, sedlex allows lexer specifications within regular Unicode. Unlike ocamllex, sedlex allows lexer specifications within regular

View File

@ -1,9 +1,8 @@
(executables (executables
(names tokenizer regressions complement subtraction repeat performance) (names tokenizer regressions complement subtraction repeat performance)
(libraries sedlex sedlex_ppx) (libraries noslop-sedlex noslop-sedlex.ppx)
(preprocess (preprocess
(pps sedlex.ppx)) (pps noslop-sedlex.ppx)))
(flags :standard -w +39))
(rule (rule
(alias runtest) (alias runtest)

View File

@ -3,7 +3,7 @@
module CSet = Sedlex_ppx.Sedlex_cset module CSet = Sedlex_ppx.Sedlex_cset
module Unicode = Sedlex_ppx.Unicode module Unicode = Sedlex_ppx.Unicode
let test_versions = ("15.0.0", "16.0.0") let test_versions = ("16.0.0", "17.0.0")
let regressions = let regressions =
[ (* Example *) [ (* Example *)

File diff suppressed because it is too large Load Diff

45
flake.lock Normal file
View File

@ -0,0 +1,45 @@
{
"nodes": {
"dragnpkgs": {
"inputs": {
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1783018382,
"narHash": "sha256-Pw7GM1fh2AnQNCitswXTHPfsswiDnEjXPN17Ge8egz4=",
"ref": "nixos-26.05",
"rev": "085892ad486428b8e51b549539a5430f0c2da1a8",
"revCount": 247,
"type": "git",
"url": "https://git.lain.faith/haskal/dragnpkgs.git"
},
"original": {
"id": "dragnpkgs",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1782847225,
"narHash": "sha256-JC9PjqKYG9ve5U8aDOLQipp3+KLANBHUvGdLZlxzdKI=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "95ca1e203c0750115fd4a6f17d5a245dfe6b1edd",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-26.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"dragnpkgs": "dragnpkgs"
}
}
},
"root": "root",
"version": 7
}

23
flake.nix Normal file
View File

@ -0,0 +1,23 @@
{
description = "Flake for noslop-sedlex";
outputs = { self, dragnpkgs } @ inputs: dragnpkgs.lib.mkFlake {
packages.default = { ocamlPackages }: ocamlPackages.callPackage ./package.nix {};
devShells.default = { ocamlPackages, stdenv, mkShell }: mkShell {
packages = with ocamlPackages; [
ocaml
dune_3
utop
odoc
alcotest
ocamlformat
] ++ (self.packages.${stdenv.hostPlatform.system}.default.propagatedBuildInputs)
++ (self.packages.${stdenv.hostPlatform.system}.default.nativeBuildInputs);
shellHook = ''
export OCAMLRUNPARAM=b
'';
};
};
}

22
licenses/LICENSE.sedlex Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright 2005, 2014 by Alain Frisch and LexiFi.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,23 +1,20 @@
# This file is generated by dune, edit dune-project instead # This file is generated by dune, edit dune-project instead
opam-version: "2.0" opam-version: "2.0"
version: "3.6" version: "3.7"
synopsis: "An OCaml lexer generator for Unicode" synopsis: "An OCaml lexer generator for Unicode"
description: """ description: """
sedlex is a lexer generator for OCaml. It is similar to ocamllex, but supports sedlex is a lexer generator for OCaml. It is similar to ocamllex, but supports
Unicode. Unlike ocamllex, sedlex allows lexer specifications within regular Unicode. Unlike ocamllex, sedlex allows lexer specifications within regular
OCaml source files. Lexing specific constructs are provided via a ppx syntax OCaml source files. Lexing specific constructs are provided via a ppx syntax
extension.""" extension."""
maintainer: ["Alain Frisch <alain.frisch@lexifi.com>"] maintainer: ["xenia <xenia@awoo.systems"]
authors: [ authors: ["xenia <xenia@awoo.systems>"]
"Alain Frisch <alain.frisch@lexifi.com>" license: "LicenseRef-Proprietary"
"https://github.com/ocaml-community/sedlex/graphs/contributors" homepage: "https://git.lain.faith/noslop/noslop-sedlex.git"
] doc: "https://git.lain.faith/noslop/noslop-sedlex/wiki"
license: "MIT"
homepage: "https://github.com/ocaml-community/sedlex"
bug-reports: "https://github.com/ocaml-community/sedlex/issues"
depends: [ depends: [
"ocaml" {>= "4.08"} "ocaml" {>= "4.08"}
"dune" {>= "3.0"} "dune" {>= "3.20"}
"ppxlib" {>= "0.26.0"} "ppxlib" {>= "0.26.0"}
"gen" "gen"
"ppx_expect" {with-test} "ppx_expect" {with-test}
@ -37,5 +34,5 @@ build: [
"@doc" {with-doc} "@doc" {with-doc}
] ]
] ]
dev-repo: "git+https://github.com/ocaml-community/sedlex.git" dev-repo: "https://git.lain.faith/noslop/noslop-sedlex.git"
doc: "https://ocaml-community.github.io/sedlex/index.html" x-maintenance-intent: ["(latest)"]

52
package.nix Normal file
View File

@ -0,0 +1,52 @@
{
lib,
fetchurl,
buildDunePackage,
gen,
ppxlib,
ppx_expect,
}: let
unicodeVersion = "17.0.0";
baseUrl = "https://www.unicode.org/Public/${unicodeVersion}";
DerivedCoreProperties = fetchurl {
url = "${baseUrl}/ucd/DerivedCoreProperties.txt";
hash = "sha256-JMf+0RlcSC+q79XB5+uCHF7h+23gfs26pktWqZ2iLAg=";
};
DerivedGeneralCategory = fetchurl {
url = "${baseUrl}/ucd/extracted/DerivedGeneralCategory.txt";
hash = "sha256-1i5bq3DKdPCZND9xIk+gUcsf3WGhq0XASIxEz8C2EC4=";
};
PropList = fetchurl {
url = "${baseUrl}/ucd/PropList.txt";
hash = "sha256-Ew3N3Kra8HEAi9/OHndD4E/fvJEIhvAX2fmskx2MZN0=";
};
in
buildDunePackage (finalAttrs: {
pname = "noslop-sedlex";
version = "3.7+DEV";
minimalOCamlVersion = "5.3";
src = ./.;
propagatedBuildInputs = [
gen
ppxlib
];
preBuild = ''
rm src/generator/data/dune
ln -s ${DerivedCoreProperties} src/generator/data/DerivedCoreProperties.txt
ln -s ${DerivedGeneralCategory} src/generator/data/DerivedGeneralCategory.txt
ln -s ${PropList} src/generator/data/PropList.txt
'';
checkInputs = [
ppx_expect
];
doCheck = true;
dontStrip = true;
})

View File

@ -1 +0,0 @@
doc: "https://ocaml-community.github.io/sedlex/index.html"

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
(* Character sets are represented as lists of intervals. The (* Character sets are represented as lists of intervals. The
intervals must be non-overlapping and not collapsable, and the list intervals must be non-overlapping and not collapsable, and the list

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
(** Representation of sets of unicode code points. *) (** Representation of sets of unicode code points. *)

View File

@ -1,3 +1,3 @@
(library (library
(name sedlex_utils) (name sedlex_utils)
(public_name sedlex.utils)) (public_name noslop-sedlex.utils))

View File

@ -1 +1 @@
https://www.unicode.org/Public/16.0.0 https://www.unicode.org/Public/17.0.0

View File

@ -1,3 +1,3 @@
(executable (executable
(name gen_unicode) (name gen_unicode)
(libraries str sedlex.utils)) (libraries str noslop-sedlex.utils))

View File

@ -1,6 +1,5 @@
(library (library
(name sedlex) (name sedlex)
(public_name sedlex) (public_name noslop-sedlex)
(wrapped false) (wrapped false)
(libraries gen) (libraries gen))
(flags :standard -w +A-4-9 -safe-string))

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
exception InvalidCodepoint of int exception InvalidCodepoint of int
exception MalFormed exception MalFormed
@ -455,7 +454,7 @@ module Utf8 = struct
let p = let p =
((n1 land 0x0f) lsl 12) lor ((n2 land 0x3f) lsl 6) lor (n3 land 0x3f) ((n1 land 0x0f) lsl 12) lor ((n2 land 0x3f) lsl 6) lor (n3 land 0x3f)
in in
if p >= 0xd800 && p <= 0xdf00 then raise MalFormed; if p >= 0xd800 && p <= 0xdfff then raise MalFormed;
p p
let check_four n1 n2 n3 n4 = let check_four n1 n2 n3 n4 =

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
(** Runtime support for lexers generated by [sedlex]. *) (** Runtime support for lexers generated by [sedlex]. *)

View File

@ -1,13 +1,11 @@
(library (library
(name sedlex_ppx) (name sedlex_ppx)
(public_name sedlex.ppx) (public_name noslop-sedlex.ppx)
(kind ppx_rewriter) (kind ppx_rewriter)
(libraries ppxlib sedlex sedlex.utils) (libraries ppxlib noslop-sedlex noslop-sedlex.utils)
(ppx_runtime_libraries sedlex) (ppx_runtime_libraries noslop-sedlex)
(preprocess (preprocess
(pps ppxlib.metaquot)) (pps ppxlib.metaquot)))
(flags
(:standard -w -9)))
(rule (rule
(targets unicode.ml) (targets unicode.ml)

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
open Ppxlib open Ppxlib
open Ast_builder.Default open Ast_builder.Default
@ -198,14 +197,14 @@ let best_final final =
let state_fun state = Printf.sprintf "__sedlex_state_%i" state let state_fun state = Printf.sprintf "__sedlex_state_%i" state
let call_state lexbuf auto state = let call_state lexbuf auto state =
let trans, final = auto.(state) in let { Sedlex.trans; finals } = auto.(state) in
if Array.length trans = 0 then ( if Array.length trans = 0 then (
match best_final final with match best_final finals with
| Some i -> eint ~loc:default_loc i | Some i -> eint ~loc:default_loc i
| None -> assert false) | None -> assert false)
else appfun (state_fun state) [lexbuf] else appfun (state_fun state) [lexbuf]
let gen_state (lexbuf_name, lexbuf) auto i (trans, final) = let gen_state (lexbuf_name, lexbuf) auto i { Sedlex.trans; finals } =
let loc = default_loc in let loc = default_loc in
let partition = Array.map fst trans in let partition = Array.map fst trans in
let cases = let cases =
@ -235,7 +234,7 @@ let gen_state (lexbuf_name, lexbuf) auto i (trans, final) =
~expr:(Exp.fun_ ~loc Nolabel None lhs body); ~expr:(Exp.fun_ ~loc Nolabel None lhs body);
] ]
in in
match best_final final with match best_final finals with
| None -> ret (body ()) | None -> ret (body ())
| Some _ when Array.length trans = 0 -> [] | Some _ when Array.length trans = 0 -> []
| Some i -> | Some i ->
@ -249,25 +248,19 @@ let gen_recflag auto =
in states with no further transitions. *) in states with no further transitions. *)
try try
Array.iter Array.iter
(fun (trans_i, _) -> (fun { Sedlex.trans; _ } ->
Array.iter Array.iter
(fun (_, j) -> (fun (_, j) ->
let trans_j, _ = auto.(j) in if Array.length auto.(j).Sedlex.trans > 0 then raise Exit)
if Array.length trans_j > 0 then raise Exit) trans)
trans_i)
auto; auto;
Nonrecursive Nonrecursive
with Exit -> Recursive with Exit -> Recursive
let gen_definition ((_, lexbuf) as lexbuf_with_name) l error = let gen_definition ((_, lexbuf) as lexbuf_with_name) auto l error =
let loc = default_loc in let loc = default_loc in
let brs = Array.of_list l in
let auto = Sedlex.compile (Array.map fst brs) in
let cases = let cases =
Array.to_list List.mapi (fun i (_, e) -> case ~lhs:(pint ~loc i) ~guard:None ~rhs:e) l
(Array.mapi
(fun i (_, e) -> case ~lhs:(pint ~loc i) ~guard:None ~rhs:e)
brs)
in in
let states = Array.mapi (gen_state lexbuf_with_name auto) auto in let states = Array.mapi (gen_state lexbuf_with_name auto) auto in
let states = List.flatten (Array.to_list states) in let states = List.flatten (Array.to_list states) in
@ -332,21 +325,21 @@ let rec repeat r = function
| n, m -> Sedlex.seq r (repeat r (n - 1, m - 1)) | n, m -> Sedlex.seq r (repeat r (n - 1, m - 1))
let regexp_of_pattern env = let regexp_of_pattern env =
let rec char_pair_op func name ~encoding p tuple = let rec char_pair_op func name ~encoding ~loc tuple =
(* Construct something like Sub(a,b) *) (* Construct something like Sub(a,b) *)
match tuple with match tuple with
| Some { ppat_desc = Ppat_tuple [p0; p1] } -> begin | Some { ppat_desc = Ppat_tuple [p0; p1]; _ } ->
match func (aux ~encoding p0) (aux ~encoding p1) with begin match func (aux ~encoding p0) (aux ~encoding p1) with
| Some r -> r | Some r -> r
| None -> | None ->
err p.ppat_loc err loc
"the %s operator can only applied to single-character length \ "the %s operator can only applied to single-character length \
regexps" regexps"
name name
end end
| _ -> | _ ->
err p.ppat_loc "the %s operator requires two arguments, like %s(a,b)" err loc "the %s operator requires two arguments, like %s(a,b)" name
name name name
and aux ~encoding p = and aux ~encoding p =
(* interpret one pattern node *) (* interpret one pattern node *)
match p.ppat_desc with match p.ppat_desc with
@ -355,18 +348,18 @@ let regexp_of_pattern env =
List.fold_left List.fold_left
(fun r p -> Sedlex.seq r (aux ~encoding p)) (fun r p -> Sedlex.seq r (aux ~encoding p))
(aux ~encoding p) pl (aux ~encoding p) pl
| Ppat_construct ({ txt = Lident "Star" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Star"; _ }, Some (_, p)) ->
Sedlex.rep (aux ~encoding p) Sedlex.rep (aux ~encoding p)
| Ppat_construct ({ txt = Lident "Plus" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Plus"; _ }, Some (_, p)) ->
Sedlex.plus (aux ~encoding p) Sedlex.plus (aux ~encoding p)
| Ppat_construct ({ txt = Lident "Utf8" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Utf8"; _ }, Some (_, p)) ->
aux ~encoding:Utf8 p aux ~encoding:Utf8 p
| Ppat_construct ({ txt = Lident "Latin1" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Latin1"; _ }, Some (_, p)) ->
aux ~encoding:Latin1 p aux ~encoding:Latin1 p
| Ppat_construct ({ txt = Lident "Ascii" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Ascii"; _ }, Some (_, p)) ->
aux ~encoding:Ascii p aux ~encoding:Ascii p
| Ppat_construct | Ppat_construct
( { txt = Lident "Rep" }, ( { txt = Lident "Rep"; _ },
Some Some
( _, ( _,
{ {
@ -377,10 +370,12 @@ let regexp_of_pattern env =
{ {
ppat_desc = ppat_desc =
Ppat_constant (i1 as i2) | Ppat_interval (i1, i2); Ppat_constant (i1 as i2) | Ppat_interval (i1, i2);
_;
}; };
]; ];
} ) ) -> begin _;
match (i1, i2) with } ) ) ->
begin match (i1, i2) with
| Pconst_integer (i1, _), Pconst_integer (i2, _) -> | Pconst_integer (i1, _), Pconst_integer (i2, _) ->
let i1 = int_of_string i1 in let i1 = int_of_string i1 in
let i2 = int_of_string i2 in let i2 = int_of_string i2 in
@ -388,33 +383,33 @@ let regexp_of_pattern env =
else err p.ppat_loc "Invalid range for Rep operator" else err p.ppat_loc "Invalid range for Rep operator"
| _ -> | _ ->
err p.ppat_loc "Rep must take an integer constant or interval" err p.ppat_loc "Rep must take an integer constant or interval"
end end
| Ppat_construct ({ txt = Lident "Rep" }, _) -> | Ppat_construct ({ txt = Lident "Rep"; _ }, _) ->
err p.ppat_loc "the Rep operator takes 2 arguments" err p.ppat_loc "the Rep operator takes 2 arguments"
| Ppat_construct ({ txt = Lident "Opt" }, Some (_, p)) -> | Ppat_construct ({ txt = Lident "Opt"; _ }, Some (_, p)) ->
Sedlex.alt Sedlex.eps (aux ~encoding p) Sedlex.alt Sedlex.eps (aux ~encoding p)
| Ppat_construct ({ txt = Lident "Compl" }, arg) -> begin | Ppat_construct ({ txt = Lident "Compl"; _ }, arg) ->
match arg with begin match arg with
| Some (_, p0) -> begin | Some (_, p0) ->
match Sedlex.compl (aux ~encoding p0) with begin match Sedlex.compl (aux ~encoding p0) with
| Some r -> r | Some r -> r
| None -> | None ->
err p.ppat_loc err p.ppat_loc
"the Compl operator can only applied to a \ "the Compl operator can only applied to a \
single-character length regexp" single-character length regexp"
end end
| _ -> err p.ppat_loc "the Compl operator requires an argument" | _ -> err p.ppat_loc "the Compl operator requires an argument"
end end
| Ppat_construct ({ txt = Lident "Sub" }, arg) -> | Ppat_construct ({ txt = Lident "Sub"; _ }, arg) ->
char_pair_op ~encoding Sedlex.subtract "Sub" p char_pair_op ~encoding Sedlex.subtract "Sub" ~loc:p.ppat_loc
(Option.map (fun (_, arg) -> arg) arg) (Option.map (fun (_, arg) -> arg) arg)
| Ppat_construct ({ txt = Lident "Intersect" }, arg) -> | Ppat_construct ({ txt = Lident "Intersect"; _ }, arg) ->
char_pair_op ~encoding Sedlex.intersection "Intersect" p char_pair_op ~encoding Sedlex.intersection "Intersect" ~loc:p.ppat_loc
(Option.map (fun (_, arg) -> arg) arg) (Option.map (fun (_, arg) -> arg) arg)
| Ppat_construct ({ txt = Lident "Chars" }, arg) -> ( | Ppat_construct ({ txt = Lident "Chars"; _ }, arg) -> (
let const = let const =
match arg with match arg with
| Some (_, { ppat_desc = Ppat_constant const }) -> Some const | Some (_, { ppat_desc = Ppat_constant const; _ }) -> Some const
| _ -> None | _ -> None
in in
match const with match const with
@ -424,8 +419,8 @@ let regexp_of_pattern env =
Sedlex.chars chars Sedlex.chars chars
| _ -> | _ ->
err p.ppat_loc "the Chars operator requires a string argument") err p.ppat_loc "the Chars operator requires a string argument")
| Ppat_interval (i_start, i_end) -> begin | Ppat_interval (i_start, i_end) ->
match (i_start, i_end) with begin match (i_start, i_end) with
| Pconst_char c1, Pconst_char c2 -> | Pconst_char c1, Pconst_char c2 ->
let valid = let valid =
match encoding with match encoding with
@ -446,9 +441,9 @@ let regexp_of_pattern env =
(codepoint (int_of_string i1)) (codepoint (int_of_string i1))
(codepoint (int_of_string i2))) (codepoint (int_of_string i2)))
| _ -> err p.ppat_loc "this pattern is not a valid interval regexp" | _ -> err p.ppat_loc "this pattern is not a valid interval regexp"
end end
| Ppat_constant const -> begin | Ppat_constant const ->
match const with begin match const with
| Pconst_string (s, _, _) -> | Pconst_string (s, _, _) ->
let rev_l = rev_csets_of_string s ~loc:p.ppat_loc ~encoding in let rev_l = rev_csets_of_string s ~loc:p.ppat_loc ~encoding in
List.fold_left List.fold_left
@ -458,15 +453,55 @@ let regexp_of_pattern env =
| Pconst_integer (i, _) -> | Pconst_integer (i, _) ->
Sedlex.chars (Cset.singleton (codepoint (int_of_string i))) Sedlex.chars (Cset.singleton (codepoint (int_of_string i)))
| _ -> err p.ppat_loc "this pattern is not a valid regexp" | _ -> err p.ppat_loc "this pattern is not a valid regexp"
end end
| Ppat_var { txt = x } -> begin | Ppat_var { txt = x; _ } ->
try StringMap.find x env begin try StringMap.find x env
with Not_found -> err p.ppat_loc "unbound regexp %s" x with Not_found -> err p.ppat_loc "unbound regexp %s" x
end end
| _ -> err p.ppat_loc "this pattern is not a valid regexp" | _ -> err p.ppat_loc "this pattern is not a valid regexp"
in in
aux ~encoding:Ascii aux ~encoding:Ascii
let handle_sedlex_match ~env ~map_rhs match_expr =
let lexbuf =
match match_expr with
| { pexp_desc = Pexp_match (lexbuf, _); _ } -> (
match lexbuf with
| { pexp_desc = Pexp_ident { txt = Lident txt; _ }; _ } ->
(txt, lexbuf)
| _ ->
err lexbuf.pexp_loc
"the matched expression must be a single identifier")
| _ ->
err match_expr.pexp_loc
"the %%sedlex extension is only recognized on match expressions"
in
let cases =
match match_expr with
| { pexp_desc = Pexp_match (_, cases); _ } -> cases
| _ -> assert false
in
let cases = List.rev cases in
let error =
match List.hd cases with
| { pc_lhs = [%pat? _]; pc_rhs = e; pc_guard = None } -> map_rhs e
| { pc_lhs = p; _ } ->
err p.ppat_loc "the last branch must be a catch-all error case"
in
let cases = List.rev (List.tl cases) in
let cases =
List.map
(function
| { pc_lhs = p; pc_rhs = e; pc_guard = None } ->
(regexp_of_pattern env p, map_rhs e)
| { pc_guard = Some e; _ } ->
err e.pexp_loc "'when' guards are not supported")
cases
in
let brs = Array.of_list cases in
let auto = Sedlex.compile (Array.map fst brs) in
(gen_definition lexbuf auto cases error, auto)
let previous = ref [] let previous = ref []
let regexps = ref [] let regexps = ref []
let should_set_cookies = ref false let should_set_cookies = ref false
@ -481,37 +516,11 @@ let mapper =
method! expression e = method! expression e =
match e with match e with
| [%expr [%sedlex [%e? { pexp_desc = Pexp_match (lexbuf, cases) }]]] -> | [%expr [%sedlex [%e? { pexp_desc = Pexp_match _; _ } as match_expr]]]
let lexbuf = ->
match lexbuf with fst (handle_sedlex_match ~env ~map_rhs:this#expression match_expr)
| { pexp_desc = Pexp_ident { txt = Lident txt } } ->
(txt, lexbuf)
| _ ->
err lexbuf.pexp_loc
"the matched expression must be a single identifier"
in
let cases = List.rev cases in
let error =
match List.hd cases with
| { pc_lhs = [%pat? _]; pc_rhs = e; pc_guard = None } ->
this#expression e
| { pc_lhs = p } ->
err p.ppat_loc
"the last branch must be a catch-all error case"
in
let cases = List.rev (List.tl cases) in
let cases =
List.map
(function
| { pc_lhs = p; pc_rhs = e; pc_guard = None } ->
(regexp_of_pattern env p, this#expression e)
| { pc_guard = Some e } ->
err e.pexp_loc "'when' guards are not supported")
cases
in
gen_definition lexbuf cases error
| [%expr | [%expr
let [%p? { ppat_desc = Ppat_var { txt = name } }] = let [%p? { ppat_desc = Ppat_var { txt = name; _ }; _ }] =
[%sedlex.regexp? [%p? p]] [%sedlex.regexp? [%p? p]]
in in
[%e? body]] -> [%e? body]] ->
@ -531,7 +540,7 @@ let mapper =
(List.map (List.map
(function (function
| [%stri | [%stri
let [%p? { ppat_desc = Ppat_var { txt = name } }] = let [%p? { ppat_desc = Ppat_var { txt = name; _ }; _ }] =
[%sedlex.regexp? [%p? p]]] as i -> [%sedlex.regexp? [%p? p]]] as i ->
regexps := i :: !regexps; regexps := i :: !regexps;
mapper := !mapper#define_regexp name p; mapper := !mapper#define_regexp name p;
@ -556,7 +565,7 @@ let mapper =
let pre_handler cookies = let pre_handler cookies =
previous := previous :=
match Driver.Cookies.get cookies "sedlex.regexps" Ast_pattern.__ with match Driver.Cookies.get cookies "sedlex.regexps" Ast_pattern.__ with
| Some { pexp_desc = Pexp_extension (_, PStr l) } -> l | Some { pexp_desc = Pexp_extension (_, PStr l); _ } -> l
| Some _ -> assert false | Some _ -> assert false
| None -> [] | None -> []

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
module Cset = Sedlex_cset module Cset = Sedlex_cset
@ -25,7 +24,7 @@ let new_node () =
let seq r1 r2 succ = r1 (r2 succ) let seq r1 r2 succ = r1 (r2 succ)
let is_chars final = function let is_chars final = function
| { eps = []; trans = [(c, f)] } when f == final -> Some c | { eps = []; trans = [(c, f)]; _ } when f == final -> Some c
| _ -> None | _ -> None
let chars c succ = let chars c succ =
@ -117,6 +116,9 @@ let transition (state : state) =
Array.sort (fun (c1, _) (c2, _) -> compare c1 c2) t; Array.sort (fun (c1, _) (c2, _) -> compare c1 c2) t;
t t
type dfa_state = { trans : (Cset.t * int) array; finals : bool array }
type dfa = dfa_state array
let compile rs = let compile rs =
let rs = Array.map compile_re rs in let rs = Array.map compile_re rs in
let counter = ref 0 in let counter = ref 0 in
@ -131,7 +133,7 @@ let compile rs =
let trans = transition state in let trans = transition state in
let trans = Array.map (fun (p, t) -> (p, aux t)) trans in let trans = Array.map (fun (p, t) -> (p, aux t)) trans in
let finals = Array.map (fun (_, f) -> List.memq f state) rs in let finals = Array.map (fun (_, f) -> List.memq f state) rs in
Hashtbl.add states_def i (trans, finals); Hashtbl.add states_def i { trans; finals };
i i
in in
let init = ref [] in let init = ref [] in
@ -139,3 +141,56 @@ let compile rs =
let i = aux !init in let i = aux !init in
assert (i = 0); assert (i = 0);
Array.init !counter (Hashtbl.find states_def) Array.init !counter (Hashtbl.find states_def)
let cset_to_label cset =
let escape_dot c =
match c with
| '"' -> "\\\""
| '\\' -> "\\\\"
| '<' -> "\\<"
| '>' -> "\\>"
| _ -> String.make 1 c
in
let format_interval (lo, hi) =
if lo = -1 && hi = -1 then "EOF"
else if lo = hi then
if lo >= 32 && lo <= 126 then "'" ^ escape_dot (Char.chr lo) ^ "'"
else Printf.sprintf "U+%04X" lo
else if lo >= 32 && lo <= 126 && hi >= 32 && hi <= 126 then
"'" ^ escape_dot (Char.chr lo) ^ "'-'" ^ escape_dot (Char.chr hi) ^ "'"
else Printf.sprintf "U+%04X-U+%04X" lo hi
in
String.concat ", "
(List.map format_interval (cset : Cset.t :> (int * int) list))
let dfa_to_dot dfa =
let buf = Buffer.create 1024 in
let bprintf = Printf.bprintf in
bprintf buf "digraph {\n";
bprintf buf " rankdir=LR;\n";
bprintf buf " node [shape=circle];\n\n";
bprintf buf " _start [shape=point];\n";
bprintf buf " _start -> state0;\n\n";
Array.iteri
(fun i { trans; finals } ->
let accepted =
let acc = ref [] in
for r = Array.length finals - 1 downto 0 do
if finals.(r) then acc := r :: !acc
done;
!acc
in
(match accepted with
| [] -> bprintf buf " state%d [label=\"%d\"];\n" i i
| rules ->
bprintf buf
" state%d [label=\"%d\\n[rule %s]\", shape=doublecircle];\n" i i
(String.concat "," (List.map string_of_int rules)));
Array.iter
(fun (cset, target) ->
let label = cset_to_label cset in
bprintf buf " state%d -> state%d [label=\"%s\"];\n" i target label)
trans)
dfa;
bprintf buf "}\n";
Buffer.contents buf

View File

@ -1,6 +1,5 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
type regexp type regexp
@ -22,4 +21,8 @@ val intersection : regexp -> regexp -> regexp option
(* If each argument is a single [chars] regexp, returns a regexp (* If each argument is a single [chars] regexp, returns a regexp
which matches the intersection set. Otherwise returns [None]. *) which matches the intersection set. Otherwise returns [None]. *)
val compile : regexp array -> ((Sedlex_cset.t * int) array * bool array) array type dfa_state = { trans : (Sedlex_cset.t * int) array; finals : bool array }
type dfa = dfa_state array
val compile : regexp array -> dfa
val dfa_to_dot : dfa -> string

View File

@ -1,5 +1,4 @@
(* The package sedlex is released under the terms of an MIT-like license. *)
(* See the attached LICENSE file. *)
(* Copyright 2005, 2013 by Alain Frisch and LexiFi. *) (* Copyright 2005, 2013 by Alain Frisch and LexiFi. *)
(* Copyright 2026 by xenia <xenia@awoo.systems> *)
include Sedlex_utils.Cset include Sedlex_utils.Cset

File diff suppressed because it is too large Load Diff

8
test/codegen/dune Normal file
View File

@ -0,0 +1,8 @@
(library
(name sedlex_gen_test)
(libraries noslop-sedlex)
(inline_tests)
(enabled_if
(>= %{ocaml_version} 4.14))
(preprocess
(pps ppx_sedlex_test ppx_expect)))

120
test/codegen/test_gen.ml Normal file
View File

@ -0,0 +1,120 @@
let%expect_test "simple string match" =
(match%sedlex_test buf with "ab" | "de" -> () | _ -> ());
[%expect
{|
DOT:
digraph {
rankdir=LR;
node [shape=circle];
_start [shape=point];
_start -> state0;
state0 [label="0"];
state0 -> state1 [label="'a'"];
state0 -> state3 [label="'d'"];
state1 [label="1"];
state1 -> state2 [label="'b'"];
state2 [label="2\n[rule 0]", shape=doublecircle];
state3 [label="3"];
state3 -> state2 [label="'e'"];
}
CODE:
let rec __sedlex_state_0 buf =
match __sedlex_partition_1 (Sedlexing.__private__next_int buf) with
| 0 -> __sedlex_state_1 buf
| 1 -> __sedlex_state_3 buf
| _ -> Sedlexing.backtrack buf
and __sedlex_state_1 buf =
match __sedlex_partition_2 (Sedlexing.__private__next_int buf) with
| 0 -> 0
| _ -> Sedlexing.backtrack buf
and __sedlex_state_3 buf =
match __sedlex_partition_3 (Sedlexing.__private__next_int buf) with
| 0 -> 0
| _ -> Sedlexing.backtrack buf in
Sedlexing.start buf; (match __sedlex_state_0 buf with | 0 -> () | _ -> ())
|}]
let%expect_test "character class" =
(match%sedlex_test buf with Plus 'a' .. 'z' -> () | _ -> ());
[%expect
{|
DOT:
digraph {
rankdir=LR;
node [shape=circle];
_start [shape=point];
_start -> state0;
state0 [label="0"];
state0 -> state1 [label="'a'-'z'"];
state1 [label="1\n[rule 0]", shape=doublecircle];
state1 -> state1 [label="'a'-'z'"];
}
CODE:
let rec __sedlex_state_0 buf =
match __sedlex_partition_1 (Sedlexing.__private__next_int buf) with
| 0 -> __sedlex_state_1 buf
| _ -> Sedlexing.backtrack buf
and __sedlex_state_1 buf =
Sedlexing.mark buf 0;
(match __sedlex_partition_1 (Sedlexing.__private__next_int buf) with
| 0 -> __sedlex_state_1 buf
| _ -> Sedlexing.backtrack buf) in
Sedlexing.start buf; (match __sedlex_state_0 buf with | 0 -> () | _ -> ())
|}]
let%expect_test "multi-rule" =
(match%sedlex_test buf with
| "ab" -> ()
| "de" -> ()
| Plus '0' .. '9' -> ()
| _ -> ());
[%expect
{|
DOT:
digraph {
rankdir=LR;
node [shape=circle];
_start [shape=point];
_start -> state0;
state0 [label="0"];
state0 -> state1 [label="'0'-'9'"];
state0 -> state2 [label="'a'"];
state0 -> state4 [label="'d'"];
state1 [label="1\n[rule 2]", shape=doublecircle];
state1 -> state1 [label="'0'-'9'"];
state2 [label="2"];
state2 -> state3 [label="'b'"];
state3 [label="3\n[rule 0]", shape=doublecircle];
state4 [label="4"];
state4 -> state5 [label="'e'"];
state5 [label="5\n[rule 1]", shape=doublecircle];
}
CODE:
let rec __sedlex_state_0 buf =
match __sedlex_partition_1 (Sedlexing.__private__next_int buf) with
| 0 -> __sedlex_state_1 buf
| 1 -> __sedlex_state_2 buf
| 2 -> __sedlex_state_4 buf
| _ -> Sedlexing.backtrack buf
and __sedlex_state_1 buf =
Sedlexing.mark buf 2;
(match __sedlex_partition_2 (Sedlexing.__private__next_int buf) with
| 0 -> __sedlex_state_1 buf
| _ -> Sedlexing.backtrack buf)
and __sedlex_state_2 buf =
match __sedlex_partition_3 (Sedlexing.__private__next_int buf) with
| 0 -> 0
| _ -> Sedlexing.backtrack buf
and __sedlex_state_4 buf =
match __sedlex_partition_4 (Sedlexing.__private__next_int buf) with
| 0 -> 1
| _ -> Sedlexing.backtrack buf in
Sedlexing.start buf;
(match __sedlex_state_0 buf with | 0 -> () | 1 -> () | 2 -> () | _ -> ())
|}]

View File

@ -1,9 +1,9 @@
(library (library
(name sedlex_test) (name sedlex_test)
(libraries sedlex) (libraries noslop-sedlex)
(inline_tests (inline_tests
(deps UTF-8-test.txt)) (deps UTF-8-test.txt))
(enabled_if (enabled_if
(>= %{ocaml_version} 4.14)) (>= %{ocaml_version} 4.14))
(preprocess (preprocess
(pps sedlex.ppx ppx_expect))) (pps noslop-sedlex.ppx ppx_expect)))

6
test/ppx_test/dune Normal file
View File

@ -0,0 +1,6 @@
(library
(name ppx_sedlex_test)
(kind ppx_rewriter)
(libraries ppxlib noslop-sedlex.ppx)
(preprocess
(pps ppxlib.metaquot)))

View File

@ -0,0 +1,36 @@
open Ppxlib
module P = Sedlex_ppx.Ppx_sedlex
module S = Sedlex_ppx.Sedlex
let reset_state () =
P.partition_counter := 0;
P.table_counter := 0;
Hashtbl.clear P.partitions;
Hashtbl.clear P.tables
let clear_tables () =
Hashtbl.clear P.partitions;
Hashtbl.clear P.tables
let expand ~ctxt:_ expr =
reset_state ();
let loc = Location.none in
let code_expr, auto =
P.handle_sedlex_match ~env:P.builtin_regexps ~map_rhs:Fun.id expr
in
let code_str = Pprintast.string_of_expression code_expr in
let dot_str = S.dfa_to_dot auto in
clear_tables ();
[%expr
print_string "DOT:\n";
print_string [%e Ast_builder.Default.estring ~loc dot_str];
print_string "CODE:\n";
print_string [%e Ast_builder.Default.estring ~loc code_str];
print_newline ()]
let ext =
Extension.V3.declare "sedlex_test" Extension.Context.expression
Ast_pattern.(single_expr_payload __)
expand
let () = Driver.register_transformation "sedlex_test" ~extensions:[ext]