Can I extract a JS function body in cos?
I got a problem to resolve, to sum it up I need to extract a function body from a JS file with cos.
I already got the JS code in a string and the function name but I'm kinda stuck as to how to get my function body, I already tried a few things (regEx, counter bracket) but there are comment and strings that block them from functioning. Is there a javascript parser or a good way to do it except by creating a javascript parser?
Comments
@Stephane Devin
You can use %SyntaxColor to parse JavaScript. Here's a very simple example that reads in a JS file, parses it, and returns a JSON representation of the semantic tokens:
ClassMethod JSTokens() As %Boolean
{
#; Reading from a file, writing to a temporary stream
Set syn = ##class(%SyntaxColor).%New(), in = ##class(%Stream.FileCharacter).%New(), out = ##class(%Stream.TmpCharacter).%New()
#; Need the "K" flag to get JSON output
Do in.LinkToFile("/Users/bsaviano/Desktop/test.js"), syn.Color(in,out,"JS","K")
#; Get a %DynamicArray from the stream
Set tokens = ##class(%DynamicArray).%FromJSON(out)
#; Process JSON ...
#; JSON is of the format:
#; {
#; // The position of the token within the line
#; p: number;
#; // The length of the token
#; c: number;
#; // Language number, see %SyntaxColor::Languages()
#; l: number;
#; // Attribute number, see %SyntaxColor::Attributes()
#; s: number;
#; }[][]
#; Where there is one array per line of the source document
}
I suggest you study the class reference for %Library.SyntaxColor since it's not that easy to use.
Thanks, I will check it out right away!
after some testing I found out that multiple token have the same attribute number
As I haven't found documentation about wich token is wich attribute number I put it here.
what i found out for wich attribute number equal to wich JS token :
1 : new line or blank space / tabulation
2 : no occurence in my file
3 : no occurence in my file
4 : ( ) [ ] { } ; , : .
5 : " '
6 : comment //
7 : number
8 : no occurence in my file
9: no occurence in my file
10 : / single slash (probably to delimit regEx)
11 : ^ + | $ (in regEx)
12 : \s (maybe escaped char in regEx)
13 : g (maybe the mode of a regEx)
14 : variable, function name (call and def)
15 : =
16 : function and var keywords
and I stopped here.
@Stephane Devin
Here's a pretty-printed version of the attributes list for JavaScript:
Attribute 0: Error
Attribute 1: White Space
Attribute 2: _Tab
Attribute 3: Label
Attribute 4: Delimiter
Attribute 5: String
Attribute 6: Comment
Attribute 7: Decimal integer
Attribute 8: Hexadecimal integer
Attribute 9: Floating point number
Attribute 10: Regexp delimiter
Attribute 11: Regexp body
Attribute 12: Regexp escape sequence
Attribute 13: Regexp flags
Attribute 14: Identifier
Attribute 15: Operator
Attribute 16: Definition keyword
Attribute 17: Statement keyword
Attribute 18: Literal keyword
Attribute 19: Expression keyword
Attribute 20: Future keyword
Attribute 21: CSP extension
Attribute 22: JSON property name
Thanks that will help a lot!
Do you know the function name?
Do you need the *exact* function body (including comments, formatting etc.) or just the function code?
yes I know the name of the function and I need the full/exact function body
I have done it 😁 it need some polish but here is the code to accomplish it using the %SyntaxColor library:
Method ExtractJsFunctionBody(jsCodePath As%String, fnName As%String)
{
#; Reading from a file, writing to a temporary streamset syn = ##class(%SyntaxColor).%New(), in = ##class(%Stream.FileCharacter).%New(), out = ##class(%Stream.TmpCharacter).%New()
// set js content in content var set content = in.Read($$$MaxStringLength)
do InStream.Rewind()
#; Need the "K" flag to get JSON outputdo in.LinkToFile(jsCodePath), syn.Color(in,out,"JS","K")
#; Get a %DynamicArray from the streamset lines = ##class(%DynamicArray).%FromJSON(out)
// get list by line of jsCodeset lineList = $listfromstring(content, $char(10))
// get pos and line of function def begining set iterLines = lines.%GetIterator()
while iterLines.%GetNext(.linenumber , .line, .type) {
set value = line.%Get(1)
if ((value.%Get("s") = 16) && (value.%Get("c") = 8)){
set value = line.%Get(3)
if (value.%Get("s") = 14 ){
set tempFnName = $extract($list(lineList, linenumber + 1),value.%Get("p") + 1, value.%Get("p") + value.%Get("c"))
if (tempFnName = fnName ){
set functionLineBegin = linenumber + 1quit
}
}
}
}
// get pos of function def endingset functionLineEnd = ..braketCounter(iterLines, lineList)
//extract functiun Bodyset functionBody = $listtostring($list(lineList,functionLineBegin,functionLineEnd),$char(10))
write !!!, functionBody
}
/// Count braket and return the line of the corresponding ending one take an iterrator at the line of the begining of the search and a list of the lines
Method braketCounter(iterLines As%Iterator.AbstractIterator, lineList As%DynamicArray) As%Integer
{
set countBraket = 1while iterLines.%GetNext(.linenumber , .line, .type) { // we keep the same iterator to continue from where we whereset iterTokens = line.%GetIterator()
while iterTokens.%GetNext(.key , .value, .type){
set token = $extract($list(lineList, linenumber + 1),value.%Get("p") + 1, value.%Get("p") + value.%Get("c") )
if (value.%Get("s") = 4) {
if ( token = "{"){
set countBraket = countBraket + 1}
elseif ( token = "}"){
set countBraket = countBraket - 1}
if (countBraket = 0){
return linenumber
}
}
}
}
return"error"
}