-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathexample_tokenization.py
More file actions
executable file
·144 lines (105 loc) · 4.17 KB
/
Copy pathexample_tokenization.py
File metadata and controls
executable file
·144 lines (105 loc) · 4.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#!/usr/bin/env python3
import re
import jk_utils
from jk_utils.tokenizer2 import *
# The following pseudoconstant receives the text data we want to parse.
TEXT = """
[General Settings]
tcpPort=12345
dataDirPath=/srv/MyProgram/data
logDirPath=/home/MyHome/My Files/log
"""
# As you can see we use some kind of INI-File format here. We deliberately choose this example as tokenizing it is
# more complex than you might think on first glance: We need to distinguish between section lines and key-value-lines.
# Both need to be parsed differently: Section lines end with a "]" which is not part of the section name token while
# a value token within a key-value-pair might have a "]" as part of a token.
# That means we need to switch the way we tokenize here: As soon as we learn that we now process a section definition,
# we need to now apply the tokenization rules for section definitions. If we learn it is a regular key value pair we
# need to apply tokenization rules for key-value-pairs.
# Let's tokenize the text above. For this we are required to define a tokenizer with regular expressions.
# This tokenizer uses regular expressions that are tried one after the other in order to identify matches.
# For that a list of tuples need to be specified which can be of the following type:
# * 4 items
# * (required) the token type to use on match
# * (required) the pattern to match
# * (optional) the next state to switch to
# * (optional) a parsing delegate to create a value from the token text
# * 6 items
# * (required) the token type to use on match
# * (optional) if not None a pattern to match but that will not be part of the token content
# * (required) the pattern to match (which will be the content of the token)
# * (optional) if not None a pattern to match but that will not be part of the token content
# * (optional) the next state to switch to
# * (optional) a parsing delegate to create a value from the token text
# By default whitespaces and newlines are recognized using "SPACE" and "NEWLINE" as token type names.
#
# This tokenizer tokenizes an INI-file.
#
class IniFileTokenizer(RegExBasedTableTokenizer):
def __init__(self):
super().__init__("std", [
RegExBasedTokenizingTable("std", [
( "lparen", "\\[", "section", None ),
( "word", "[A-Za-z][a-zA-Z0-9]*", "kvp", None ),
]),
RegExBasedTokenizingTable("section", [
( "rparen", "\\]", None, None ),
( "section", r"[^]\n]+", None, None ),
]).addStateTransition("NEWLINE", "std"),
RegExBasedTokenizingTable("kvp", [
( "eq", "=", None, None ),
( "word", "[A-Za-z][a-zA-Z0-9]*", None, None ),
( "int", r"[+-]?[1-9][0-9]*|0", None, self.__parseInt ),
( "other", r"[^\n]+", None, None ),
]).addStateTransition("NEWLINE", "std"),
])
#
def __parseInt(self, rawTokenText):
return int(rawTokenText)
#
#
# Now let's test the tokenizer and produce tokens. Store them in a list.
tokenizer = IniFileTokenizer()
tokens = list(tokenizer.tokenize(TEXT))
for token in tokens:
print(token)
print()
# In order to parse the text specified above we define simple pattern schemas.
VALUE_PATTERN = TokenPatternAlternatives([
TokenPattern("int", assignToVarTyped="v").setTag("t", "int"),
TokenPattern("word", assignToVarTyped="v").setTag("t", "str"),
TokenPattern("other", assignToVarTyped="v").setTag("t", "str"),
])
KVP_PATTERN = TokenPatternSequence([
TokenPattern("word", assignToVar="k"),
TokenPattern("eq"),
VALUE_PATTERN,
])
SECTION_PATTERN = TokenPatternSequence([
TokenPattern("lparen"),
TokenPattern("section", assignToVar="s"),
TokenPattern("rparen"),
])
NL_PATTERN = TokenPattern("NEWLINE")
# bow let's parse!
def parse(tokens:list):
pos = 0
while pos < len(tokens):
bParsingSuccess, nTokensEaten, data = NL_PATTERN.tryMatch(tokens, pos)
if bParsingSuccess:
pos += nTokensEaten
continue
bParsingSuccess, nTokensEaten, data = SECTION_PATTERN.tryMatch(tokens, pos)
if bParsingSuccess:
yield data
pos += nTokensEaten
continue
bParsingSuccess, nTokensEaten, data = KVP_PATTERN.tryMatch(tokens, pos)
if bParsingSuccess:
yield data
pos += nTokensEaten
continue
raise Exception("Syntax error!")
#
for x in parse(tokens):
print(">", x)