forked from AutoHotkey/Ahk2Exe
-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathScriptParser.ahk
248 lines (219 loc) · 7.24 KB
/
ScriptParser.ahk
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
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
PreprocessScript(ByRef ScriptText, AhkScript, ExtraFiles, FileList="", FirstScriptDir="", Options="", iOption=0)
{
SplitPath, AhkScript, ScriptName, ScriptDir
if !IsObject(FileList)
{
FileList := [AhkScript]
ScriptText := "; <COMPILER: v" A_AhkVersion ">`n"
FirstScriptDir := ScriptDir
IsFirstScript := true
Options := { comm: ";", esc: "``" }
OldWorkingDir := A_WorkingDir
SetWorkingDir, %ScriptDir%
DerefIncludeVars.A_ScriptFullPath := AhkScript
DerefIncludeVars.A_ScriptName := ScriptName
DerefIncludeVars.A_ScriptDir := ScriptDir
}
oldLineFile := DerefIncludeVars.A_LineFile
DerefIncludeVars.A_LineFile := AhkScript
IfNotExist, %AhkScript%
if !iOption
Util_Error((IsFirstScript ? "Script" : "#include") " file """ AhkScript """ cannot be opened.")
else return
cmtBlock := false, contSection := false
Loop, Read, %AhkScript%
{
tline := Trim(A_LoopReadLine)
if !cmtBlock
{
if !contSection
{
if StrStartsWith(tline, Options.comm)
continue
else if tline =
continue
else if StrStartsWith(tline, "/*")
{
cmtBlock := true
continue
}
}
if StrStartsWith(tline, "(") && !IsFakeCSOpening(tline)
contSection := true
else if StrStartsWith(tline, ")")
contSection := false
tline := RegExReplace(tline, "\s+" RegExEscape(Options.comm) ".*$", "")
if !contSection && RegExMatch(tline, "i)^#Include(Again)?[ \t]*[, \t]?\s+(.*)$", o)
{
IsIncludeAgain := (o1 = "Again")
IgnoreErrors := false
IncludeFile := o2
if RegExMatch(IncludeFile, "\*[iI]\s+?(.*)", o)
IgnoreErrors := true, IncludeFile := Trim(o1)
if RegExMatch(IncludeFile, "^<(.+)>$", o)
{
if IncFile2 := FindLibraryFile(o1, FirstScriptDir)
{
IncludeFile := IncFile2
goto _skip_findfile
}
}
IncludeFile := DerefIncludePath(IncludeFile, DerefIncludeVars)
if InStr(FileExist(IncludeFile), "D")
{
SetWorkingDir, %IncludeFile%
continue
}
_skip_findfile:
IncludeFile := Util_GetFullPath(IncludeFile)
AlreadyIncluded := false
for k,v in FileList
if (v = IncludeFile)
{
AlreadyIncluded := true
break
}
if(IsIncludeAgain || !AlreadyIncluded)
{
if !AlreadyIncluded
FileList.Insert(IncludeFile)
PreprocessScript(ScriptText, IncludeFile, ExtraFiles, FileList, FirstScriptDir, Options, IgnoreErrors)
}
}else if !contSection && tline ~= "i)^FileInstall[, \t]"
{
if tline ~= "^\w+\s+(:=|\+=|-=|\*=|/=|//=|\.=|\|=|&=|\^=|>>=|<<=)"
continue ; This is an assignment!
; workaround for `, detection
EscapeChar := Options.esc
EscapeCharChar := EscapeChar EscapeChar
EscapeComma := EscapeChar ","
EscapeTmp := chr(2)
EscapeTmpD := chr(3)
StringReplace, tline, tline, %EscapeCharChar%, %EscapeTmpD%, All
StringReplace, tline, tline, %EscapeComma%, %EscapeTmp%, All
if !RegExMatch(tline, "i)^FileInstall[ \t]*[, \t][ \t]*([^,]+?)[ \t]*(,|$)", o) || o1 ~= "[^``]%"
Util_Error("Error: Invalid ""FileInstall"" syntax found. Note that the first parameter must not be specified using a continuation section.")
_ := Options.esc
StringReplace, o1, o1, %_%`%, `%, All
StringReplace, o1, o1, %_%`,, `,, All
StringReplace, o1, o1, %_%%_%,, %_%,, All
; workaround for `, detection [END]
StringReplace, o1, o1, %EscapeTmp%, `,, All
StringReplace, o1, o1, %EscapeTmpD%, %EscapeChar%, All
StringReplace, tline, tline, %EscapeTmp%, %EscapeComma%, All
StringReplace, tline, tline, %EscapeTmpD%, %EscapeCharChar%, All
ExtraFiles.Insert(o1)
ScriptText .= tline "`n"
}else if !contSection && RegExMatch(tline, "i)^#CommentFlag\s+(.+)$", o)
Options.comm := o1, ScriptText .= tline "`n"
else if !contSection && RegExMatch(tline, "i)^#EscapeChar\s+(.+)$", o)
Options.esc := o1, ScriptText .= tline "`n"
else if !contSection && RegExMatch(tline, "i)^#DerefChar\s+(.+)$", o)
Util_Error("Error: #DerefChar is not supported.")
else if !contSection && RegExMatch(tline, "i)^#Delimiter\s+(.+)$", o)
Util_Error("Error: #Delimiter is not supported.")
else
ScriptText .= (contSection ? A_LoopReadLine : tline) "`n"
}else if StrStartsWith(tline, "*/")
cmtBlock := false
}
Loop, % !!IsFirstScript ; equivalent to "if IsFirstScript" except you can break from the block
{
static AhkPath := A_IsCompiled ? A_ScriptDir "\..\AutoHotkey.exe" : A_AhkPath
IfNotExist, %AhkPath%
break ; Don't bother with auto-includes because the file does not exist
Util_Status("Auto-including any functions called from a library...")
ilibfile = %A_Temp%\_ilib.ahk
IfExist, %ilibfile%, FileDelete, %ilibfile%
AhkType := AHKType(AhkPath)
if !AhkType
Util_Error("Error: The AutoHotkey build used for auto-inclusion of library functions is not recognized.", 1, AhkPath)
if (AhkType.Era = "Legacy")
Util_Error("Error: Legacy AutoHotkey versions (prior to v1.1) are not allowed as the build used for auto-inclusion of library functions.", 1, AhkPath)
tmpErrorLog := Util_TempFile()
RunWait, "%AhkPath%" /iLib "%ilibfile%" /ErrorStdOut "%AhkScript%" 2>"%tmpErrorLog%", %FirstScriptDir%, UseErrorLevel
FileRead,tmpErrorData,%tmpErrorLog%
FileDelete,%tmpErrorLog%
if (ErrorLevel = 2)
Util_Error("Error: The script contains syntax errors.",1,tmpErrorData)
IfExist, %ilibfile%
{
PreprocessScript(ScriptText, ilibfile, ExtraFiles, FileList, FirstScriptDir, Options)
FileDelete, %ilibfile%
}
StringTrimRight, ScriptText, ScriptText, 1 ; remove trailing newline
}
DerefIncludeVars.A_LineFile := oldLineFile
if OldWorkingDir
SetWorkingDir, %OldWorkingDir%
}
IsFakeCSOpening(tline)
{
Loop, Parse, tline, %A_Space%%A_Tab%
if !StrStartsWith(A_LoopField, "Join") && InStr(A_LoopField, ")")
return true
return false
}
FindLibraryFile(name, ScriptDir)
{
libs := [ScriptDir "\Lib", A_MyDocuments "\AutoHotkey\Lib", A_ScriptDir "\..\Lib"]
p := InStr(name, "_")
if p
name_lib := SubStr(name, 1, p-1)
for each,lib in libs
{
file := lib "\" name ".ahk"
IfExist, %file%
return file
if !p
continue
file := lib "\" name_lib ".ahk"
IfExist, %file%
return file
}
}
StrStartsWith(ByRef v, ByRef w)
{
return SubStr(v, 1, StrLen(w)) = w
}
RegExEscape(t)
{
static _ := "\.*?+[{|()^$"
Loop, Parse, _
StringReplace, t, t, %A_LoopField%, \%A_LoopField%, All
return t
}
Util_TempFile(d:="")
{
if ( !StrLen(d) || !FileExist(d) )
d:=A_Temp
Loop
tempName := d "\~temp" A_TickCount ".tmp"
until !FileExist(tempName)
return tempName
}
class DerefIncludeVars
{
static A_IsCompiled := true
}
DerefIncludePath(path, vars)
{
static SharedVars := {A_AhkPath:1, A_AppData:1, A_AppDataCommon:1, A_ComputerName:1, A_ComSpec:1, A_Desktop:1, A_DesktopCommon:1, A_MyDocuments:1, A_ProgramFiles:1, A_Programs:1, A_ProgramsCommon:1, A_Space:1, A_StartMenu:1, A_StartMenuCommon:1, A_Startup:1, A_StartupCommon:1, A_Tab:1, A_Temp:1, A_UserName:1, A_WinDir:1}
p := StrSplit(path, "%")
path := p[1]
n := 2
while n < p.Length()
{
vn := p[n]
if ObjHasKey(vars, vn)
path .= vars[vn] . p[++n]
else if SharedVars[vn]
path .= %vn% . p[++n]
else
path .= "%" vn
++n
}
if (n = p.Length())
path .= "%" p[n]
return path
}