Related
I working on a language similar to ruby called gaiman and I'm using PEG.js to generate the parser.
Do you know if there is a way to implement heredocs with proper indentation?
xxx = <<<END
hello
world
END
the output should be:
"hello
world"
I need this because this code doesn't look very nice:
def foo(arg) {
if arg == "here" then
return <<<END
xxx
xxx
END
end
end
this is a function where the user wants to return:
"xxx
xxx"
I would prefer the code to look like this:
def foo(arg) {
if arg == "here" then
return <<<END
xxx
xxx
END
end
end
If I trim all the lines user will not be able to use a string with leading spaces when he wants. Does anyone know if PEG.js allows this?
I don't have any code yet for heredocs, just want to be sure if something that I want is possible.
EDIT:
So I've tried to implement heredocs and the problem is that PEG doesn't allow back-references.
heredoc = "<<<" marker:[\w]+ "\n" text:[\s\S]+ marker {
return text.join('');
}
It says that the marker is not defined. As for trimming I think I can use location() function
I don't think that's a reasonable expectation for a parser generator; few if any would be equal to the challenge.
For a start, recognising the here-string syntax is inherently context-sensitive, since the end-delimiter must be a precise copy of the delimiter provided after the <<< token. So you would need a custom lexical analyser, and that means that you need a parser generator which allows you to use a custom lexical analyser. (So a parser generator which assumes you want a scannerless parser might not be the optimal choice.)
Recognising the end of the here-string token shouldn't be too difficult, although you can't do it with a single regular expression. My approach would be to use a custom scanning function which breaks the here-string into a series of lines, concatenating them as it goes until it reaches a line containing only the end-delimiter.
Once you've recognised the text of the literal, all you need to normalise the spaces in the way you want is the column number at which the <<< starts. With that, you can trim each line in the string literal. So you only need a lexical scanner which accurately reports token position. Trimming wouldn't normally be done inside the generated lexical scanner; rather, it would be the associated semantic action. (Equally, it could be a semantic action in the grammar. But it's always going to be code that you write.)
When you trim the literal, you'll need to deal with the cases in which it is impossible, because the user has not respected the indentation requirement. And you'll need to do something with tab characters; getting those right probably means that you'll want a lexical scanner which computes visible column positions rather than character offsets.
I don't know if peg.js corresponds with those requirements, since I don't use it. (I did look at the documentation, and failed to see any indication as to how you might incorporate a custom scanner function. But that doesn't mean there isn't a way to do it.) I hope that the discussion above at least lets you check the detailed documentation for the parser generator you want to use, and otherwise find a different parser generator which will work for you in this use case.
Here is the implementation of heredocs in Peggy successor to PEG.js that is not maintained anymore. This code was based on the GitHub issue.
heredoc = "<<<" begin:marker "\n" text:($any_char+ "\n")+ _ end:marker (
&{ return begin === end; }
/ '' { error(`Expected matched marker "${begin}", but marker "${end}" was found`); }
) {
const loc = location();
const min = loc.start.column - 1;
const re = new RegExp(`\\s{${min}}`);
return text.map(line => {
return line[0].replace(re, '');
}).join('\n');
}
any_char = (!"\n" .)
marker_char = (!" " !"\n" .)
marker "Marker" = $marker_char+
_ "whitespace"
= [ \t\n\r]* { return []; }
EDIT: above didn't work with another piece of code after heredoc, here is better grammar:
{ let heredoc_begin = null; }
heredoc = "<<<" beginMarker "\n" text:content endMarker {
const loc = location();
const min = loc.start.column - 1;
const re = new RegExp(`^\\s{${min}}`, 'mg');
return {
type: 'Literal',
value: text.replace(re, '')
};
}
__ = (!"\n" !" " .)
marker 'Marker' = $__+
beginMarker = m:marker { heredoc_begin = m; }
endMarker = "\n" " "* end:marker &{ return heredoc_begin === end; }
content = $(!endMarker .)*
Doesn't seem like iTunes connect has basic exporting of user emails from Prerelease > External testers
Needed to export emails to CSV
Does anyone have a script or workaround solution?
Thanks!
You can use pilot (from Fastlane) to do that :
To export in a CSV file: fastlane pilot export
$ fastlane pilot list
+--------+--------+--------------------------+-----------+
| Internal Testers |
+--------+--------+--------------------------+-----------+
| First | Last | Email | # Devices |
+--------+--------+--------------------------+-----------+
| Felix | Krause | felix#krausefx.com | 2 |
+--------+--------+--------------------------+-----------+
+-----------+---------+----------------------------+-----------+
| External Testers |
+-----------+---------+----------------------------+-----------+
| First | Last | Email | # Devices |
+-----------+---------+----------------------------+-----------+
| Max | Manfred | email#email.com | 0 |
| Detlef | Müller | detlef#krausefx.com | 1 |
+-----------+---------+----------------------------+-----------+
Just use console get Email, First Name, Last Name
Paste this into console
https://gist.github.com/creaoy/80d1092283a5d0fa1070
Upon trial and error (because I'm desperate), I found a way out, the low tech way.
Go to Activity > iOS History > All Builds > Testers
This is the page where you see all of your external and internal testers' emails, name, status, sessions, crashes and devices. Select all rows and paste them into excel. It will display all information in each cell nicely :) Select the first column in excel and you have all the emails only.
I expanded creaoy's script to include the Status (Notified/Installed x.xx). Still works for me today.
Scroll all the way down so all testers are loaded. Then paste this in Safari's error console and hit enter.
var text = '';
$('.col-email').each(function(index,el) {
if (index == 0) {
text = 'Email, First Name, Last Name, Status\n';
}
else {
//Email
text = text + $.trim($(el).find("a").text()) + ',';
//First Name
text = text + $.trim($($($($('.col-name')[index]).find("span")[0]).find("span")[0]).text()) + ',';
//Last Name
text = text + $.trim($($($($('.col-name')[index]).find("span")[0]).find("span")[1]).text()) + ',';
//Status
text = text + $.trim($($($($('.col-status')[index]).find("div")[0]).find("span")[0]).text()) + '\n';
}
});
var a = document.createElement("a");
var file = new Blob([text], {type: 'text/csv'});
a.href = URL.createObjectURL(file);
a.download = name; a.click();
Not as yet. You can only import a csv file but not create one from the users there.
You can copy the rows, edit them in TextEdit/Notepad in the format:
fistname,lastname,email
and save that as csv to use when you want to import those emails later.
It would be good if they implemented it to do this automatically, or at least having the option to send the updates to specific groups that you can create.
I made a little console hack that extracts just the emails. Improve to your liking :-)
https://gist.github.com/larsparendt/a67d25b1611db67ba67b
Some great answers, but wanted to add another option. BoardingBot is a tool that lets you send TestFlight invites automatically, as well as emails to your beta testers. So this might fill your need for contacting your beta testers.
Disclaimer: I'm the founder of BoardingBot :)
Since iTunes Connect is now an angular app, many of the answers on this page no longer work. However if you have the Angular/Batarang extension for Chrome you can paste this script in the console (cmd+i) and it will spit out a csv. Note that first you have to inspect an item in the list (cmd + shift + c) to get a reference to $scope. Also don't forget to scroll down to make the page auto-load the whole list.
var text = '';
angular.forEach($scope.filteredTesters, function(val) {
text += val.firstName.value + ',';
text += val.lastName.value + ',';
text += val.emailAddress.value + '\n';
});
var a = document.createElement("a");
var file = new Blob([text], {type: 'text/csv'});
a.href = URL.createObjectURL(file);
a.download = name; a.click();
Edit:
The below method is flakey - there is a great ruby gem for managing testflight testers called Fastlane pilot. I woudl recommend looking into that instead: https://github.com/fastlane/fastlane/tree/master/pilot
Found a google chrome extension that will do the job:
Table capture:https://chrome.google.com/webstore/detail/table-capture/iebpjdmgckacbodjpijphcplhebcmeop/reviews?hl=en
Also needed to filter out duplicates, which can be done using Google spreadsheets
If you just need the emails and have a text editor with regex replacement you can do this
Scroll to the bottom of the list in itunesconnect so you get all rows
Copy all the rows into text editor of your choice (I used sublime text)
replace ^.*\n\n with <empty string>. This removes all names
replace \n\s\n with ,.
replace \n with ,.
and you're left with a CSV of all the emails
if like me you also needed the date this is the code you need:
var text = '';
$('.col-email').each(function(index,el) {
if (index == 0) {
text = 'Email, First Name, Last Name, Status, Date\n';
}
else {
//Email
text = text + $.trim($(el).find("a").text()) + ',';
//First Name
text = text + $.trim($($($($('.col-name')[index]).find("span")[0]).find("span")[0]).text()) + ',';
//Last Name
text = text + $.trim($($($($('.col-name')[index]).find("span")[0]).find("span")[1]).text()) + ',';
//Status
text = text + $.trim($($($($('.col-status')[index]).find("div")[0]).find("span")[0]).text()) + ',';
//Date
text = text + '\"' + $.trim($($($($('.col-status')[index]).find("div")[0]).find("span")[2]).text()) + '\"\n';
}
});
var a = document.createElement("a");
var file = new Blob([text], {type: 'text/csv'});
a.href = URL.createObjectURL(file);
a.download = name; a.click();
I came up with a jQuery solution to output this data to the console in CSV format. It is dependent on the way that the pages selectors are set up but as of now (Sep. 2017), it runs great.
Open up the console on your browser (I use Chrome), copy and paste this code in, hit enter. Quick and dirty.
Edit
I borrowed some code from guido's answer here that triggers an automatic download after hitting enter.
output = "email,first name,last name\n";
jQuery('table.table-itc-testers tr[ng-repeat*="itcUser"]').each(function(){
row = [];
//Email Address
row.push(jQuery(this).find('span a[href*="users_roles"]').text());
// First and last name
full_name = jQuery(this).find('td.sorted > span:not(.ng-hide)');
// If only name is filled out
if(full_name==""){
row.push("");
row.push("");
} else {
row.push(full_name.clone().children().remove().end().text().trim());
row.push(full_name.find('span.lastname').text());
}
output += row.join(",") + "\n";
});
var a = document.createElement("a");
var file = new Blob([output], {type: 'text/csv'});
a.href = URL.createObjectURL(file);
a.download = 'external_testers.csv'; a.click();
Quick Update with a new answer: iTunes Connect now has an easy-to-use download button on the right side of the page!
So i'm busy with AutoIt, i am now using this code
_IEPropertySet($passwordnew, "innertext", "12345678910")
This will paste the text "12345678910" into the Textbox of the webpage, is it possible to let it type it letter for letter? Not that it paste the whole concept. I already tried several stuff but it resulted in many errors.
Why? You can try something like this:
Global $passwordnew = StringSplit(12345678910, '', 2)
Global $valueOfPasswortField = ''
For $i = 0 To UBound($passwordnew) - 1
;~ $valueOfPasswortField &= $passwordnew[$i]
;~ ConsoleWrite($valueOfPasswortField & #CRLF)
_IEPropertySet($passwordnew, "innertext", _IEPropertyGet($passwordnew, "innertext") & $passwordnew[$i])
Sleep(400)
Next
It seems to me that you need a OnKeyUp event fired
$password = "1234567890"
_IEPropertySet($passwordnew, "innertext", "12345678910")
BlockInput(1)
_IEAction($passwordnew, "focus")
Send($password, 1)
BlockInput(0)
or
_IEPropertySet($passwordnew, "innertext", "12345678910")
$passwordnew.fireEvent("onkeyup")
I have a php feedback form that I want the comment text to be aligned to left or right depending on language:
if Arabic should be aligned rtl, any other language no (default which is ltr)
This is the current code line (that makes everything right to left!):
$comment = "<p dir=\"rtl\">".str_replace ("\n", "<br />", $comment);
so I want to tell it: if Arabic, then rtl, if not Arabic, ignore that rtl
function containsArabic($str)
{
return preg_match('~\p{Arabic}~u', $str);
}
echo containsArabic('helloسلام'); // rturn 1
echo containsArabic('سلام');//return 1
echo containsArabic('testسلامtest'); // return 1
echo containsArabic('test');// return 0
if the text contain Arabic letter the function return 1 else return 0
You can use these regex'es to determine whether the comments contain Arabic or Persian chars.
function containsArabic($str)
{
return preg_match('~\p{Arabic}~u', $str);
}
echo containsArabic('helloسلام');
echo containsArabic('سلام');
echo containsArabic('testسلامtest');
echo containsArabic('test');
using grep, vim's grep, or another unix shell command, I'd like to find the functions in a large cpp file that contain a specific word in their body.
In the files that I'm working with the word I'm looking for is on an indented line, the corresponding function header is the first line above the indented line that starts at position 0 and is not a '{'.
For example searching for JOHN_DOE in the following code snippet
int foo ( int arg1 )
{
/// code
}
void bar ( std::string arg2 )
{
/// code
aFunctionCall( JOHN_DOE );
/// more code
}
should give me
void bar ( std::string arg2 )
The algorithm that I hope to catch in grep/vim/unix shell scripts would probably best use the indentation and formatting assumptions, rather than attempting to parse C/C++.
Thanks for your suggestions.
I'll probably get voted down for this!
I am an avid (G)VIM user but when I want to review or understand some code I use Source Insight. I almost never use it as an actual editor though.
It does exactly what you want in this case, e.g. show all the functions/methods that use some highlighted data type/define/constant/etc... in a relations window...
(source: sourceinsight.com)
Ouch! There goes my rep.
As far as I know, this can't be done. Here's why:
First, you have to search across lines. No problem, in vim adding a _ to a character class tells it to include new lines. so {_.*} would match everything between those brackets across multiple lines.
So now you need to match whatever the pattern is for a function header(brittle even if you get it to work), then , and here's the problem, whatever lines are between it and your search string, and finally match your search string. So you might have a regex like
/^\(void \+\a\+ *(.*)\)\_.*JOHN_DOE
But what happens is the first time vim finds a function header, it starts matching. It then matches every character until it finds JOHN_DOE. Which includes all the function headers in the file.
So the problem is that, as far as I know, there's no way to tell vim to match every character except for this regex pattern. And even if there was, a regex is not the tool for this job. It's like opening a beer with a hammer. What we should do is write a simple script that gives you this info, and I have.
fun! FindMyFunction(searchPattern, funcPattern)
call search(a:searchPattern)
let lineNumber = line(".")
let lineNumber = lineNumber - 1
"call setpos(".", [0, lineNumber, 0, 0])
let lineString = getline(lineNumber)
while lineString !~ a:funcPattern
let lineNumber = lineNumber - 1
if lineNumber < 0
echo "Function not found :/"
endif
let lineString = getline(lineNumber)
endwhile
echo lineString
endfunction
That should give you the result you want and it's way easier to share, debug, and repurpose than a regular expression spit from the mouth of Cthulhu himself.
Tough call, although as a starting point I would suggest this wonderful VIM Regex Tutorial.
You cannot do that reliably with a regular expression, because code is not a regular language. You need a real parser for the language in question.
Arggh! I admit this is a bit over the top:
A little program to filter stdin, strip comments, and put function bodies on the same line. It'll get fooled by namespaces and function definitions inside class declarations, besides other things. But it might be a good start:
#include <stdio.h>
#include <assert.h>
int main() {
enum {
NORMAL,
LINE_COMMENT,
MULTI_COMMENT,
IN_STRING,
} state = NORMAL;
unsigned depth = 0;
for(char c=getchar(),prev=0; !feof(stdin); prev=c,c=getchar()) {
switch(state) {
case NORMAL:
if('/'==c && '/'==prev)
state = LINE_COMMENT;
else if('*'==c && '/'==prev)
state = MULTI_COMMENT;
else if('#'==c)
state = LINE_COMMENT;
else if('\"'==c) {
state = IN_STRING;
putchar(c);
} else {
if(('}'==c && !--depth) || (';'==c && !depth)) {
putchar(c);
putchar('\n');
} else {
if('{'==c)
depth++;
else if('/'==prev && NORMAL==state)
putchar(prev);
else if('\t'==c)
c = ' ';
if(' '==c && ' '!=prev)
putchar(c);
else if(' '<c && '/'!=c)
putchar(c);
}
}
break;
case LINE_COMMENT:
if(' '>c)
state = NORMAL;
break;
case MULTI_COMMENT:
if('/'==c && '*'==prev) {
c = '\0';
state = NORMAL;
}
break;
case IN_STRING:
if('\"'==c && '\\'!=prev)
state = NORMAL;
putchar(c);
break;
default:
assert(!"bug");
}
}
putchar('\n');
return 0;
}
Its c++, so just it in a file, compile it to a file named 'stripper', and then:
cat my_source.cpp | ./stripper | grep JOHN_DOE
So consider the input:
int foo ( int arg1 )
{
/// code
}
void bar ( std::string arg2 )
{
/// code
aFunctionCall( JOHN_DOE );
/// more code
}
The output of "cat example.cpp | ./stripper" is:
int foo ( int arg1 ) { }
void bar ( std::string arg2 ){ aFunctionCall( JOHN_DOE ); }
The output of "cat example.cpp | ./stripper | grep JOHN_DOE" is:
void bar ( std::string arg2 ){ aFunctionCall( JOHN_DOE ); }
The job of finding the function name (guess its the last identifier to precede a "(") is left as an exercise to the reader.
For that kind of stuff, although it comes to primitive searching again, I would recommend compview plugin. It will open up a search window, so you can see the entire line where the search occured and automatically jump to it. Gives a nice overview.
(source: axisym3.net)
Like Robert said Regex will help. In command mode start a regex search by typing the "/" character followed by your regex.
Ctags1 may also be of use to you. It can generate a tag file for a project. This tag file allows a user to jump directly from a function call to it's definition even if it's in another file using "CTRL+]".
u can use grep -r -n -H JOHN_DOE * it will look for "JOHN_DOE" in the files recursively starting from the current directory
you can use the following code to practically find the function which contains the text expression:
public void findFunction(File file, String expression) {
Reader r = null;
try {
r = new FileReader(file);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
BufferedReader br = new BufferedReader(r);
String match = "";
String lineWithNameOfFunction = "";
Boolean matchFound = false;
try {
while(br.read() > 0) {
match = br.readLine();
if((match.endsWith(") {")) ||
(match.endsWith("){")) ||
(match.endsWith("()")) ||
(match.endsWith(")")) ||
(match.endsWith("( )"))) {
// this here is because i guessed that method will start
// at the 0
if((match.charAt(0)!=' ') && !(match.startsWith("\t"))) {
lineWithNameOfFunction = match;
}
}
if(match.contains(expression)) {
matchFound = true;
break;
}
}
if(matchFound)
System.out.println(lineWithNameOfFunction);
else
System.out.println("No matching function found");
} catch (IOException ex) {
ex.printStackTrace();
}
}
i wrote this in JAVA, tested it and works like a charm. has few drawbacks though, but for starters it's fine. didn't add support for multiple functions containing same expression and maybe some other things. try it.