Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathnlpengine.py
More file actions
Latest commit
202 lines (176 loc) · 9.23 KB
/
Copy pathnlpengine.py
File metadata and controls
202 lines (176 loc) · 9.23 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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
importshutil
importsubprocess
importos
importjson
classNLPEngine:
def__init__(self, engineDir, analyzersDir ):
self.engineDir=engineDir
self.analyzersDir=analyzersDir
defanalyzerPath(self, analyzerFolder):
returnos.path.join(self.analyzersDir, analyzerFolder)
defkbPath(self, analyzerFolder):
returnos.path.join(self.analyzerPath(analyzerFolder), "kb", "user")
defputJsonFile(self, analyzerFolder, jsonPath, name=None):
"""Place a JSON file in the analyzer's kb/user directory so its
json2kbb pass converts it to a KBB on the next run. The file is copied
to <analyzer>/kb/user/<name>.json (name defaults to the source file's
own name; a .json extension is appended if missing). Returns the
destination path."""
ifnotos.path.isfile(jsonPath):
raiseFileNotFoundError(f"JSON file not found: {jsonPath}")
withopen(jsonPath, "r", encoding="utf-8") asfh:
json.load(fh) # validate it is JSON before copying
target=nameifnameelseos.path.basename(jsonPath)
ifnottarget.lower().endswith(".json"):
target+=".json"
kbdir=self.kbPath(analyzerFolder)
os.makedirs(kbdir, exist_ok=True)
dest=os.path.join(kbdir, target)
shutil.copyfile(jsonPath, dest)
returndest
defputJsonObject(self, analyzerFolder, obj, name):
"""Write a JSON-serializable value to the analyzer's kb/user directory
so its json2kbb pass converts it to a KBB on the next run. Serialized
to <analyzer>/kb/user/<name>.json (a .json extension is appended if
missing). Returns the destination path."""
target=nameifname.lower().endswith(".json") elsename+".json"
kbdir=self.kbPath(analyzerFolder)
os.makedirs(kbdir, exist_ok=True)
dest=os.path.join(kbdir, target)
withopen(dest, "w", encoding="utf-8") asfh:
json.dump(obj, fh, ensure_ascii=False, indent=2)
returndest
defspecPath(self, analyzerFolder):
returnos.path.join(self.analyzerPath(analyzerFolder), "spec")
defoutputDir(self, analyzerFolder, textPath):
returnos.path.join(self.analyzerPath(analyzerFolder), "input", textPath+"_log")
defoutputFileContents(self, analyzerFolder, filename, outputFile):
outputPath=os.path.join(self.outputDir(analyzerFolder, filename), outputFile)
withopen(outputPath, "r") asfile:
contents=file.read()
returncontents
definputFileDir(self, analyzerFolder, textPath):
returnos.path.join(self.analyzerPath(analyzerFolder), "input", textPath)
defanalyzeFile(self, analyzerFolder, textPath, dev=False, compiled=False):
"""Run nlp.exe over textPath using the analyzer at analyzerFolder.
If compiled=True, passes -COMPILED so the engine loads the
analyzer's pre-built shared libraries (bin/run.<ext> + bin/kb.<ext>)
instead of running interpreted from the .nlp source. Build
those libraries first via compileAnalyzer() / compileLocal() or
by running the platform's scripts/compile-analyzer.{sh,ps1}
directly.
"""
self.clearLogFiles(analyzerFolder)
analyzerPath=os.path.join(self.analyzersDir, analyzerFolder)
textPath=os.path.join(analyzerPath, "input", textPath)
try:
executable_path=os.path.join(self.engineDir, "nlp.exe")
args= [executable_path, "-ANA", analyzerPath, "-WORK", self.engineDir, textPath]
ifdev:
args.append("-DEV")
ifcompiled:
args.append("-COMPILED")
withopen("output.txt", "w") asoutput_file, open("errors.txt", "w") aserror_file:
subprocess.run(args, stdout=output_file, stderr=error_file, text=True)
exceptsubprocess.CalledProcessErrorase:
print(f"An error occurred: {e}")
return
defcompileAnalyzer(self, analyzerFolder, inputTextPath=None, kbOnly=False,
analyzerOnly=False):
"""Generate the C++ source trees for the named analyzer.
Runs nlp.exe in -COMPILE mode, or -COMPILEKB if kbOnly=True
(KB only), or -COMPILEANA if analyzerOnly=True (analyzer rules
only, skipping the KB). -COMPILE emits <analyzer>/run/*.cpp +
<analyzer>/kb/*.cpp; -COMPILEKB emits just <analyzer>/kb/*.cpp;
-COMPILEANA emits just <analyzer>/run/*.cpp. The resulting trees
still need to be built into shared libraries before analyzeFile
with compiled=True will work — use compileLocal() to drive the
local cmake build via scripts/compile-analyzer.sh.
Use analyzerOnly=True when only the rules changed and the KB is
already compiled. kbOnly and analyzerOnly are mutually exclusive.
inputTextPath: any input text file path; -COMPILE requires one
but doesn't actually analyze the text. If None, defaults to the
analyzer's input/ directory's first text file (if any).
"""
analyzerPath=os.path.join(self.analyzersDir, analyzerFolder)
ifinputTextPathisNone:
inputDir=os.path.join(analyzerPath, "input")
ifos.path.isdir(inputDir):
forentryinsorted(os.listdir(inputDir)):
candidate=os.path.join(inputDir, entry)
ifos.path.isfile(candidate):
inputTextPath=candidate
break
ifinputTextPathisNoneornotos.path.isfile(inputTextPath):
raiseFileNotFoundError(
"compileAnalyzer needs an input text file path "
"(none provided and analyzer's input/ has no files)"
)
ifkbOnlyandanalyzerOnly:
raiseValueError("compileAnalyzer: kbOnly and analyzerOnly are mutually exclusive")
try:
executable_path=os.path.join(self.engineDir, "nlp.exe")
flag="-COMPILEKB"ifkbOnlyelse ("-COMPILEANA"ifanalyzerOnlyelse"-COMPILE")
args= [executable_path, flag, "-ANA", analyzerPath,
"-WORK", self.engineDir, inputTextPath]
subprocess.run(args, check=True, text=True)
exceptsubprocess.CalledProcessErrorase:
print(f"compileAnalyzer failed: {e}")
raise
returnanalyzerPath
defcompileLocal(self, analyzerFolder, inputTextPath, kbOnly=False,
analyzerOnly=False, ubuntu="ubuntu-latest"):
"""Run scripts/compile-analyzer.sh to build the analyzer's
compiled shared libraries locally via cmake.
Calls into the shell script in the engine repo's scripts/ dir,
which runs nlp.exe -COMPILE first then drives cmake against the
engine's bundled compile-libs. On success, drops
<analyzer>/bin/run.so + bin/runu.so + bin/kb.so + bin/kbu.so
(or just bin/kb.so + bin/kbu.so for kbOnly, or just
bin/run.so + bin/runu.so for analyzerOnly).
Use analyzerOnly=True when only the rules changed and the KB is
already compiled. kbOnly and analyzerOnly are mutually exclusive.
After this returns, analyzeFile(..., compiled=True) will load
the staged libraries.
"""
ifkbOnlyandanalyzerOnly:
raiseValueError("compileLocal: kbOnly and analyzerOnly are mutually exclusive")
analyzerPath=os.path.join(self.analyzersDir, analyzerFolder)
script=os.path.join(self.engineDir, "scripts", "compile-analyzer.sh")
ifnotos.path.isfile(script):
raiseFileNotFoundError(
f"compile-analyzer.sh not found at {script}"
)
args= ["bash", script]
ifkbOnly:
args.append("--kb-only")
elifanalyzerOnly:
args.append("--analyzer-only")
args.extend([analyzerPath, inputTextPath, ubuntu])
subprocess.run(args, check=True, text=True)
returnos.path.join(analyzerPath, "bin")
defanalyzeStr(self, analyzerFolder, filename, textStr):
inputPath=self.inputFileDir(analyzerFolder,filename)
withopen(inputPath, "w") asinput_file:
input_file.write(textStr)
self.analyzeFile(analyzerFolder, filename, False)
defisAnalyzerFolder(self, analyzerFolder):
required_folders= ['spec', 'input', 'kb/user']
forfolderinrequired_folders:
ifnotos.path.isdir(os.path.join(self.analyzersDir, analyzerFolder, folder)):
returnFalse
returnTrue
defclearLogFiles(self, analyzerFolder):
logPath=os.path.join(os.path.join(self.analyzersDir,analyzerFolder), "input")
forroot, dirs, filesinos.walk(logPath):
fordirindirs:
ifdir.endswith("_log"):
shutil.rmtree(os.path.join(root, dir))
defcreateInputDir(self, analyzer, inputFolder, clearFolder=True):
inputPath=os.path.join(self.analyzerPath(analyzer), "input", inputFolder)
ifclearFolderandos.path.exists(inputPath):
shutil.rmtree(inputPath)
shutil.os.makedirs(inputPath)
ifnotos.path.exists(inputPath):
os.makedirs(inputPath)
returninputPath