llparse 0.3.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. {llparse-0.3.0 → llparse-0.3.1}/PKG-INFO +1 -1
  2. {llparse-0.3.0 → llparse-0.3.1}/llparse/__init__.py +1 -1
  3. {llparse-0.3.0 → llparse-0.3.1}/llparse/capi_builder.py +113 -10
  4. {llparse-0.3.0 → llparse-0.3.1}/llparse/llparse.py +21 -21
  5. {llparse-0.3.0 → llparse-0.3.1}/llparse.egg-info/PKG-INFO +1 -1
  6. {llparse-0.3.0 → llparse-0.3.1}/llparse.egg-info/SOURCES.txt +1 -0
  7. llparse-0.3.1/tests/test_capi.py +121 -0
  8. {llparse-0.3.0 → llparse-0.3.1}/LICENSE +0 -0
  9. {llparse-0.3.0 → llparse-0.3.1}/README.md +0 -0
  10. {llparse-0.3.0 → llparse-0.3.1}/llparse/C_compiler.py +0 -0
  11. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/__init__.py +0 -0
  12. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/__init__.pyi +0 -0
  13. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/__main__.py +0 -0
  14. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/_looper.py +0 -0
  15. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/_looper.pyi +0 -0
  16. {llparse-0.3.0 → llparse-0.3.1}/llparse/_tempita/compat3.py +0 -0
  17. {llparse-0.3.0 → llparse-0.3.1}/llparse/compilator.py +0 -0
  18. {llparse-0.3.0 → llparse-0.3.1}/llparse/constants.py +0 -0
  19. {llparse-0.3.0 → llparse-0.3.1}/llparse/debug.py +0 -0
  20. {llparse-0.3.0 → llparse-0.3.1}/llparse/dot.py +0 -0
  21. {llparse-0.3.0 → llparse-0.3.1}/llparse/enumerator.py +0 -0
  22. {llparse-0.3.0 → llparse-0.3.1}/llparse/errors.py +0 -0
  23. {llparse-0.3.0 → llparse-0.3.1}/llparse/frontend.py +0 -0
  24. {llparse-0.3.0 → llparse-0.3.1}/llparse/header.py +0 -0
  25. {llparse-0.3.0 → llparse-0.3.1}/llparse/pybuilder/__init__.py +0 -0
  26. {llparse-0.3.0 → llparse-0.3.1}/llparse/pybuilder/builder.py +0 -0
  27. {llparse-0.3.0 → llparse-0.3.1}/llparse/pybuilder/loopchecker.py +0 -0
  28. {llparse-0.3.0 → llparse-0.3.1}/llparse/pybuilder/main_code.py +0 -0
  29. {llparse-0.3.0 → llparse-0.3.1}/llparse/pybuilder/parsemap.py +0 -0
  30. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/__init__.py +0 -0
  31. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/code.py +0 -0
  32. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/implementation.py +0 -0
  33. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/namespace.py +0 -0
  34. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/nodes.py +0 -0
  35. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/peephole.py +0 -0
  36. {llparse-0.3.0 → llparse-0.3.1}/llparse/pyfront/transform.py +0 -0
  37. {llparse-0.3.0 → llparse-0.3.1}/llparse/spanalloc.py +0 -0
  38. {llparse-0.3.0 → llparse-0.3.1}/llparse/trie.py +0 -0
  39. {llparse-0.3.0 → llparse-0.3.1}/llparse.egg-info/dependency_links.txt +0 -0
  40. {llparse-0.3.0 → llparse-0.3.1}/llparse.egg-info/requires.txt +0 -0
  41. {llparse-0.3.0 → llparse-0.3.1}/llparse.egg-info/top_level.txt +0 -0
  42. {llparse-0.3.0 → llparse-0.3.1}/pyproject.toml +0 -0
  43. {llparse-0.3.0 → llparse-0.3.1}/setup.cfg +0 -0
  44. {llparse-0.3.0 → llparse-0.3.1}/tests/test_compilator.py +0 -0
  45. {llparse-0.3.0 → llparse-0.3.1}/tests/test_frontend.py +0 -0
  46. {llparse-0.3.0 → llparse-0.3.1}/tests/test_loop_checker.py +0 -0
  47. {llparse-0.3.0 → llparse-0.3.1}/tests/test_span_allocator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llparse
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A Parody of llparse written for writing C Parsers with Python
5
5
  Author-email: Vizonex <VizonexBusiness@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -8,5 +8,5 @@ A Pythonic version of the typescript llparse library.
8
8
  from .dot import Dot
9
9
  from .llparse import LLParse
10
10
 
11
- __version__ = "0.3.0"
11
+ __version__ = "0.3.1"
12
12
  __all__ = ("Dot", "LLParse")
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  import re # inspired by rust's bindgen clang compiler
7
7
  import sys
8
+ from pathlib import Path
8
9
  from contextlib import contextmanager
9
10
  from dataclasses import dataclass, field
10
11
  from enum import IntEnum
@@ -50,7 +51,7 @@ API_C_CODE = Template("""
50
51
 
51
52
  #include "{{header}}"
52
53
 
53
- /* Prevent Interfearing with other parsers (example: llhttp) */
54
+ /* Prevent Interfearing with other parsers (example: {{prefix}}) */
54
55
  #define {{upper}}_CALLBACK_MAYBE(PARSER, NAME) \\
55
56
  do { \\
56
57
  {{prefix}}_settings_t* settings; \\
@@ -73,7 +74,7 @@ API_C_CODE = Template("""
73
74
  err = settings->NAME((PARSER), (START), (LEN)); \\
74
75
  } while (0)
75
76
 
76
- /* Custom Underrived from llhttp */
77
+ /* Custom Underrived from {{prefix}} */
77
78
  #define {{upper}}_VALUE_CALLBACK_MAYBE(PARSER, NAME, VALUE) \\
78
79
  do { \\
79
80
  {{prefix}}_settings_t* settings; \\
@@ -123,6 +124,15 @@ void {{prefix}}_settings_init({{prefix}}_settings_t* settings) {
123
124
  memset(settings, 0, sizeof(*settings));
124
125
  }
125
126
 
127
+ int {{prefix}}_execute({{prefix}}_t* parser, const char* data, size_t len) {
128
+ return {{prev_prefix}}_execute(parser, data, data + len);
129
+ }
130
+
131
+ {{if extra_code}}
132
+ /* EXTRAS */
133
+ {{extra_code}}
134
+ {{endif}}
135
+
126
136
  """)
127
137
 
128
138
 
@@ -134,6 +144,7 @@ API_C_HEADER = Template(
134
144
  extern "C" {
135
145
  #endif
136
146
  #include <stddef.h>
147
+ #include <stdint.h>
137
148
 
138
149
  #if defined(__wasm__)
139
150
  #define {{export}} __attribute__((visibility("default")))
@@ -161,20 +172,36 @@ typedef int (*{{prefix}}_value_cb)({{prefix}}_t*, int value);
161
172
  struct {{prefix}}_settings_s {
162
173
  {{if spans}}
163
174
  /* Spans */
164
- {{for _, name in spans}}{{prefix}}_data_cb {{name}};{{endfor}}
175
+ {{for _, name in spans}}
176
+ {{prefix}}_data_cb {{name}};
177
+ {{endfor}}
165
178
 
166
179
  {{endif}}
167
180
  {{if values}}
168
181
  /* Value Callbacks */
169
- {{for _, name in values}}{{prefix}}_value_cb {{name}};{{endfor}}
182
+ {{for _, name in values}}
183
+ {{prefix}}_value_cb {{name}};
184
+ {{endfor}}
170
185
 
171
186
  {{endif}}
172
187
  {{if matches}}
173
188
  /* Callbacks */
174
- {{for _, name in matches}}{{prefix}}_cb {{name}};{{endfor}}
189
+ {{for _, name in matches}}
190
+ {{prefix}}_cb {{name}};
191
+ {{endfor}}
175
192
  {{endif}}
176
193
  };
177
194
 
195
+ {{export}}
196
+ void {{prefix}}_settings_init({{prefix}}_settings_t* settings);
197
+
198
+ {{export}}
199
+ int {{prefix}}_execute({{prefix}}_t* parser, const char* data, size_t len);
200
+
201
+ {{if extra_code}}
202
+ {{extra_code}}
203
+ {{endif}}
204
+
178
205
  #ifdef __cplusplus
179
206
  } /* extern "C" */
180
207
  #endif
@@ -268,7 +295,7 @@ class CythonWriter(CodeWriter):
268
295
 
269
296
  class Filter:
270
297
  """A Filter to go off for match, value, and spans
271
- to help organize the output for an llhttp-like C
298
+ to help organize the output for an {{prefix}}-like C
272
299
  Library"""
273
300
 
274
301
  __slots__ = ("_pattern", "_is_re", "_use")
@@ -302,10 +329,10 @@ class Filter:
302
329
  def is_match(self, name: str) -> bool:
303
330
  """Determines if the source matches up"""
304
331
  if self._is_re:
305
- return re.search(self._pattern) is not None
332
+ return re.search(self._pattern, name) is not None
306
333
  else:
307
334
  # it's most likely a prefix of ignorable callbacks...
308
- return name.startswith(self._pattern)
335
+ return name.startswith(self._pattern) or self._pattern == name
309
336
 
310
337
  def edit_name(self, name: str) -> str:
311
338
  """Ran when use is enabled"""
@@ -374,7 +401,7 @@ class UsedResults:
374
401
  raise TypeError(f"unknown type for name:{name} type:{ty}")
375
402
 
376
403
 
377
- @dataclass
404
+ @dataclass(slots=True)
378
405
  class Results:
379
406
  ignore: IgnoredResults = field(default_factory=IgnoredResults)
380
407
  """Seperates into a non-api file under a .c suffix these
@@ -395,6 +422,73 @@ class CAPIResult:
395
422
  c: str
396
423
  header: str
397
424
 
425
+ def write(self, c: Path | str, header:Path | str) -> None:
426
+ """
427
+ Writes the output to the chosen file locations
428
+
429
+ :param c: Output for where to write the C File
430
+ :type c: Path | str
431
+ :param header: Output for where to write the Header File
432
+ :type header: Path | str
433
+ """
434
+ Path(c).write_text(self.c)
435
+ Path(header).write_text(self.header)
436
+
437
+
438
+ # Undecided on implementing yet...
439
+ # class Getter:
440
+ # """A property that can be defined and obtained with a C wrapper"""
441
+
442
+ # def __init__(
443
+ # self,
444
+ # name: str,
445
+ # reset: bool = False,
446
+ # init: bool = False,
447
+ # recast_type:str | None = None
448
+ # ) -> None:
449
+ # self.name = name
450
+ # self.reset = reset
451
+ # self.init = init
452
+ # self.recast_type = recast_type
453
+
454
+
455
+ # class ErrnoGetter(Getter):
456
+ # """
457
+ # Creates a list of errors that your parser can raise
458
+ # if you provided enums originally.
459
+ # """
460
+
461
+ # def __init__(
462
+ # self,
463
+ # ty: type[IntEnum],
464
+ # recast_type: str | None = None,
465
+ # pause:IntEnum | None= None,
466
+ # ) -> None:
467
+ # self.pause = pause
468
+ # self.emap = {k:v.value for k, v in ty._member_map_.items()}
469
+
470
+ # super().__init__(name="error", reset=False, init=False, recast_type=recast_type)
471
+
472
+ # def write_enum(self, prefix:str) -> str:
473
+ # code = f"enum {prefix}_errno" + "{"
474
+ # code += ",\n".join([f" {k} = {v}" for k, v in self.emap.items()])
475
+ # code += "\n};"
476
+ # code += f"typedef enum {prefix}_errno {prefix}_errno_t;\n"
477
+ # return code
478
+
479
+ # def write_map(self, prefix:str):
480
+ # code = "#define " + prefix.upper() + "_ERRNO_MAP(XX) \\\n"
481
+ # for v, k in self.emap.items():
482
+ # code += f"XX({v}, {k}, {k}) \\\n"
483
+ # code += "\n\n"
484
+ # return code
485
+
486
+
487
+
488
+
489
+
490
+
491
+
398
492
 
399
493
  class LibraryCompiler:
400
494
  """Used for writing in what callbacks to mark as wrappable or don't use
@@ -403,7 +497,7 @@ class LibraryCompiler:
403
497
 
404
498
  __slots__ = ("_filters", "_dummy_ignore", "_prefix", "_previous_prefix")
405
499
 
406
- def __init__(self, prefix: str, llparse: LLParse):
500
+ def __init__(self, prefix: str, llparse: LLParse) -> None:
407
501
  self._filters: set[Filter] = set()
408
502
  # Put all ignored spans, matches and values here...
409
503
  self._dummy_ignore = Filter("dummy", False)
@@ -491,6 +585,8 @@ class LibraryCompiler:
491
585
  root: builder.Node,
492
586
  header: str | None = None,
493
587
  headerguard: str | None = None,
588
+ extra_header_code:str | None = None,
589
+ extra_c_code:str | None = None
494
590
  ) -> CAPIResult:
495
591
  """
496
592
  Compile library's data and create a header file and other external for the library's c-api
@@ -501,6 +597,10 @@ class LibraryCompiler:
501
597
  :type header: str | None
502
598
  :param headerguard: The Macro's name for the Header guard
503
599
  :type headerguard: str | None
600
+ :param extra_header_code: Applies extra code to the header file if extra functions or things were required
601
+ :type extra_header_code:str | None
602
+ :param extra_c_code: Applies extra code to the c file if extra functions or things were required
603
+ :type extra_c_code:str | None
504
604
  :return: The CAPI Result containing extra files for the c-wrapper
505
605
  :rtype: CAPIResult
506
606
 
@@ -515,6 +615,8 @@ class LibraryCompiler:
515
615
  spans=used.spans,
516
616
  upper=self._prefix.upper(),
517
617
  header=header or (self._prefix + ".h"),
618
+ prev_prefix=self._previous_prefix,
619
+ extra_code=extra_c_code
518
620
  )
519
621
  header_code = API_C_HEADER.substitute(
520
622
  prefix=self._prefix,
@@ -525,5 +627,6 @@ class LibraryCompiler:
525
627
  export=self._prefix.upper() + "_EXPORT",
526
628
  headerguard=headerguard or f"{self._prefix.upper()}_CAPI_INCLUDE",
527
629
  prev_prefix=self._previous_prefix,
630
+ extra_code=extra_header_code
528
631
  )
529
632
  return CAPIResult(api_code, header_code)
@@ -1,6 +1,5 @@
1
- """"""
2
-
3
1
  from dataclasses import dataclass
2
+ from pathlib import Path
4
3
 
5
4
  from .C_compiler import CCompiler
6
5
  from .frontend import (
@@ -14,34 +13,35 @@ from .header import HeaderBuilder
14
13
  from .capi_builder import LibraryCompiler
15
14
 
16
15
 
17
- @dataclass
16
+ @dataclass(slots=True)
18
17
  class CompilerResult:
19
18
  c: str
20
19
  """Textual C code"""
21
20
  header: str
22
21
  """Textual C header file"""
23
22
 
24
- # NOTE Coming soon... I will making a settings compiler which is attached to CompilerResult to Compile your own
25
- # settings structures and be able to concate it to the existing header file like in llhttp
26
- # I'm also in the works of adding a Cython .pxd import as well...
23
+ def write(self, c: Path | str, header:Path | str) -> None:
24
+ """
25
+ Writes the output to the chosen file locations
27
26
 
27
+ :param c: Output for where to write the C File
28
+ :type c: Path | str
29
+ :param header: Output for where to write the Header File
30
+ :type header: Path | str
31
+ """
32
+ Path(c).write_text(self.c)
33
+ Path(header).write_text(self.header)
34
+
28
35
 
36
+ # It's just small enough to write the compile as a dataclass
37
+ @dataclass(slots=True)
29
38
  class Compiler:
30
39
  """Used to Compile C code together"""
31
-
32
- def __init__(
33
- self,
34
- prefix: str,
35
- headerGuard: str | None = None,
36
- debug: str | None = None,
37
- maxTableElemWidth: int | None = None,
38
- minTableSize: int | None = None,
39
- ):
40
- self.prefix = prefix
41
- self.headerGuard = headerGuard
42
- self.debug = debug
43
- self.maxTableElemWidth = maxTableElemWidth
44
- self.minTableSize = minTableSize
40
+ prefix: str
41
+ headerGuard: str | None = None
42
+ debug: str | None = None
43
+ maxTableElemWidth: int | None = None
44
+ minTableSize: int | None = None
45
45
 
46
46
  def to_frontend(
47
47
  self,
@@ -167,6 +167,6 @@ class LLParse(source.Builder):
167
167
  minTableSize if minTableSize else DEFAULT_MIN_TABLE_SIZE,
168
168
  ).to_frontend(root, self.properties)
169
169
 
170
- def capi(self, prefix: str):
170
+ def capi(self, prefix: str) -> LibraryCompiler:
171
171
  """Using a new prefix this tool enables helping build c-api wrapper that is simillar to llhttp"""
172
172
  return LibraryCompiler(prefix, self)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: llparse
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: A Parody of llparse written for writing C Parsers with Python
5
5
  Author-email: Vizonex <VizonexBusiness@gmail.com>
6
6
  Requires-Python: >=3.10
@@ -38,6 +38,7 @@ llparse/pyfront/namespace.py
38
38
  llparse/pyfront/nodes.py
39
39
  llparse/pyfront/peephole.py
40
40
  llparse/pyfront/transform.py
41
+ tests/test_capi.py
41
42
  tests/test_compilator.py
42
43
  tests/test_frontend.py
43
44
  tests/test_loop_checker.py
@@ -0,0 +1,121 @@
1
+ """
2
+ Tests tools for writing C-API Wrappers
3
+ """
4
+ from llparse import LLParse
5
+ import re
6
+
7
+ import pytest
8
+
9
+ DUMMY_HEADER = """#ifndef LLPARSE_CAPI_INCLUDE
10
+ #define LLPARSE_CAPI_INCLUDE
11
+ #ifdef __cplusplus
12
+ extern "C" {
13
+ #endif
14
+ #include <stddef.h>
15
+ #include <stdint.h>
16
+
17
+ #if defined(__wasm__)
18
+ #define LLPARSE_EXPORT __attribute__((visibility("default")))
19
+ #elif defined(_WIN32)
20
+ #define LLPARSE_EXPORT __declspec(dllexport)
21
+ #else
22
+ #define LLPARSE_EXPORT
23
+ #endif
24
+
25
+ typedef llparse_internal_t llparse_t;
26
+ typedef struct llparse_settings_s llparse_settings_t;
27
+ typedef int (*llparse_data_cb)(llparse_t*, const char* at, size_t length);
28
+ typedef int (*llparse_cb)(llparse_t*);
29
+
30
+ struct llparse_settings_s {
31
+ /* Spans */
32
+ llparse_data_cb llparse_on_span;
33
+ /* Callbacks */
34
+ llparse_cb llparse_on_test;
35
+ };
36
+
37
+ LLPARSE_EXPORT
38
+ void llparse_settings_init(llparse_settings_t* settings);
39
+
40
+ LLPARSE_EXPORT
41
+ int llparse_execute(llparse_t* parser, const char* data, size_t len);
42
+
43
+ #ifdef __cplusplus
44
+ } /* extern "C" */
45
+ #endif
46
+
47
+ #endif /* LLPARSE_CAPI_INCLUDE */
48
+ """
49
+
50
+
51
+
52
+ @pytest.fixture()
53
+ def llparse() -> LLParse:
54
+ return LLParse("llparse_internal")
55
+
56
+ def test_collecting_spans(llparse:LLParse):
57
+ lc = llparse.capi("llparse")
58
+ span = llparse.span(llparse.code.span("span"))
59
+ start = llparse.node("start")
60
+ body = llparse.node("body")
61
+
62
+ start.otherwise(span.start(body))
63
+
64
+ body.skipTo(span.end(start))
65
+
66
+ lc.use("span")
67
+ result = lc.filter(start)
68
+ assert result.use.spans, "No spans found"
69
+
70
+
71
+ def test_collecting_matches(llparse:LLParse):
72
+ lc = llparse.capi("lc")
73
+ span = llparse.span(llparse.code.span("llparse_on_span"))
74
+ on_test = llparse.code.match("llparse_on_test")
75
+
76
+ start = llparse.node("start")
77
+ body = llparse.node("body")
78
+
79
+ start.otherwise(
80
+ span.start(body)
81
+ )
82
+
83
+ body.skipTo(
84
+ span.end(
85
+ llparse.invoke(on_test, {0:start}, llparse.error(-1, "error"))
86
+ )
87
+ )
88
+ lc.use("llparse_")
89
+ result = lc.filter(start)
90
+ assert result.use.spans, "No spans found"
91
+ assert result.use.matches, "No matches found"
92
+
93
+
94
+ def test_write_capi(llparse:LLParse):
95
+ lc = llparse.capi("llparse")
96
+ span = llparse.span(llparse.code.span("llparse_on_span"))
97
+ on_test = llparse.code.match("llparse_on_test")
98
+
99
+ start = llparse.node("start")
100
+ body = llparse.node("body")
101
+
102
+ start.otherwise(
103
+ span.start(body)
104
+ )
105
+
106
+ body.skipTo(
107
+ span.end(
108
+ llparse.invoke(on_test, {0:start}, llparse.error(-1, "error"))
109
+ )
110
+ )
111
+
112
+ lc.use("span")
113
+ lc.use_regex(r"llparse_([^\s]+)")
114
+
115
+
116
+ result = lc.build(start)
117
+ assert result.header.strip() == DUMMY_HEADER.strip()
118
+
119
+
120
+
121
+
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes