scc 2+2 4
Expression evaluated and printed:
scc 2+2 4
If expression is not terminated with semicolon, it is sent to std::cout. Same for last expression in multi-statement snippet. If snippet have bash special characters, it must be in single or double quotes. If expression starts with minus, end-of-options indicator -- must be used:
scc '"hello world"' hello world scc -- -42 -42 scc 'double x=0.5; sin(x)' 0.479426 scc 'bitset<64>(unsigned(-1))' 0000000000000000000000000000000011111111111111111111111111111111 echo "ABC" | scc 'char c; while (cin.get(c)) cout << (char)tolower(c);' abc
Snippet is evaluated in environment where all STL includes are included and almost all STL objects are imported into default namespace with using std::...;
If you need only plain C++ REPL, that is about all you need to know. You can skip to Install section now. Sections that follow are about SCC C++ extensions to alleviate C++ verbosity and AWK like capabilities.
Instead of C++ std::cout printing, SCC uses so called bar-print.
Below are bar-print statements with equivalent code in comments:
_ x; // cout << x; __ x; // cout << x << endl; __ x << y; // cout << x << y << endl; __ x, y; // cout << x << " " << y << endl;
Standard library includes std::ostream_iterator. Unlike std::cout it accept only one specific type (specified at construction time) and it is not pre-defined object. So to use it with STL algorithms, you need call its quite verbose constructor.
Let say we have vector<int> V; and set<string> S; and we want copy these to std::cout. With standard library:
copy(V.begin(), V.end(), ostream_iterator<int>(cout, " ")); copy(S.begin(), S.end(), ostream_iterator<int>(cout, " "));
Include print.h defines oi object, which is similarly to std::cout, can work with any type and is pre-defined. With oi, above example will be:
copy(V.begin(), V.end(), oi); copy(S.begin(), S.end(), oi);
Assigning a container to oi is equivalent to doing std::copy, so above example we could have also written as:
oi = V; oi = S;
Any STL container can be also printed without oi. You can just send it to std::cout or to bar-print.
In comments is equivalent code:
vint C {1,2,3}; __ C; // vector<int> C{1,2,3};
// cout << "{";
// for(auto it=C.begin(); it!=C.end()-1; it++)
// cout << *it << ", ";
// if(!C.empty()) cout << C.back();
// cout << "}\n";
// prints: {1, 2, 3}
int C[] {1,2,3}; __ C; // {1,2,3}
array<int,3> C {1,2,3}; __ C; // {1,2,3}
tuple<int,int> C {1,2}; __ C; // ⟨1, 2⟩
map<int,int> C {{1,1},{2,2}}; __ C; // {⟨1,1⟩, ⟨2,2⟩}
vector<vint> C {{1,1},{2,2}}; __ C; // {{1, 1}, {2, 2}}
Pre-defined object in can be used as initialiser. When a value is read from in, it is acquired from std::cin.
int i(in); // int i; cin >> i; float x = 1.1 + float(in); // float y; cin >> y; float x = 1.1 + y;
|
|
Object in does not check for EOF. With scalar objects use only in context where you can ignore or do not expect EOF. |
If a container have non-zero length, we can directly input from std::cin without object in:
vint V(10); // vector<int> V(10); cin >> V; // for(size_t i=0; i<V.size(); i++) cin >> V[i];
Object in can be used to input container too, but we need somehow to tell it how many elements needs to be read.
If container have zero length, we tell object in how many object to read:
vint V = in(10);
Google CodeJam and FaceBook Hackercup often have input format to input an array where first goes a number (N) followed by N elements of an array. To read it, we can use:
int N(in); // int N; cin >> N; vint V = in(N); // vector<int> V(N); for(int i=0; i<N; i++) cin >> V[i];
Or even shorter:
vint V = in(in); // 1st will be evaluated 'in' in parenthesis, 2nd - 'in.operator()()'
If container have zero length (is empty) and we didn’t specified how many elements to read, container is filled up until EOF.
echo 1 2 3 | scc 'vint V = in; V' // Note: we can not use: vint V(in);
{1,2,3}
File print.h can be used as standalone, independent include.
#include <print.h>
int main() {
__ "hello world";
}
Files simple.h, cj.h and stl.h can also be used independently.
This is for those who likes terse C-style code. Below is string manipulation example. Given Thello world? string, it transforms it and prints - " H e l l o w o r l d ! ":
dchar S; // deque<char> S;
S <= "Thello world?"; // const char cS[]="Thello world?";
// for (int i; i<(sizeof cS); i++)
// S.push_back(cS[i])
--S; // S.pop_front();
S--; // S.pop_back(); // '\0'
++S = 'H'; // S.front() = 'H';
S++ = '!'; // S.back() = '!';
'"' >> S << '"' << '\n'; // S.push_front('"');
// S.push_back('"');
// S.push_back('\n');
copy(+S, -S, oi); // copy(S.begin(), S.end(), ostream_iterator<char>(cout, " "));
All operators do not create temp copy, they are modifying target container itself. Full list of container operators:
+C // C.begin()
-C // C.end()
!C // C.size()
++C // C.front()
C++ // C.back()
--C // C.pop_front()
C-- // C.pop_back()
C << x // C.push_back(x)
x >> C // C.push_front(x)
C >> x // x = C.back(x); C.pop_back();
x << C // x = C.front(x); C.pop_front();
C1 << C2 // copy(C2.begin(), C2.end(), back_inserter(C1));
C1 >> C2 // copy(C1.rbegin(), C1.rend(), front_inserter(C2));
C1 <= C2 // C1.clear(); copy(C2.begin(), C2.end(), back_inserter(C1));
++Pair // Pair.first
Pair++ // Pair.second
++Tuple // get<0>(Tuple)
Tuple++ // get<tuple_size<tuple<Types...> >::value-1>(Tuple)
!Tuple // tuple_size<decltype(Tuple) >::value
// STACK
S << x // S.push(x)
S ++ // S.top(x)
S >> x // x = S.top(x); S.pop()
S -- // S.pop(x)
// QUEUE
Q++ // Q.back(x)
++Q // Q.front(x)
--Q // Q.pop(x)
Q << x // Q.push(x)
x << Q // x = Q.front(x); Q.pop()
Shortcuts are typedefs and macros to cut verbosity. For example:
str --> std::string vint --> std::vector<int> WRL --> while(read_line())
Some variables are pre-defined:
i, j, k, n, m — long, initialized to 0
x, y, z — double, initialized to 0
s, w — std::string
c — char
You don’t need to remember these, you can re-define these to what ever you want. So, in last example we could’ve skipped definition char c;.
Square an array. C++11 is on by default:
scc 'vint V{1,2,3}; for(auto v:V) v*=v; V'
{1, 4, 9}
Calculate words frequencies from stdin. (w - is pre-defined std::string)
echo aa bb aa | scc 'map<str,int> M; while(cin>>w) M[w]++; M'
{⟨aa,2⟩, ⟨bb,1⟩}
SCC also can be used as tool for stream processing - something like native AWK. Syntax is not exactly AWK’s, it is still C++ , but it is quite similar.
Biggest difference is script layout. AWK’s script have following elements (simplified):
awk 'BEGIN{begin-expr}; {per-record-expr}; END{end-expr}'
SCC have two alternatives for above. First is explicit while-loop:
scc 'begin-expr; WRL per-record-expr; end-expr;'
Shortcut WRL expands to while(read_line()). Function read_line(), reads input line and splits it into fields.
Second alternative is to use options -n and -p. With -n, record is read, split into fields and snippet is evaluated for every record. With -p, additionally all fields are printed after snippet was evaluated. These are equivalent to PERL’s and are convenient when we do not have begin-expr and end-expr.
scc -n 'per-record-expr;'
Fortunately, GCC allows use of $ in identifiers, so AWK’s dollar variables ($0, $1, $NF) are valid in SCC.
In SCC, $ variables are of special string type fld, it is similar to std::string but it can be used in arithmetic expressions - they will be implicitly converted to corresponding numeric type. And it can be assigned a numeric value. That is fld behave like AWK’s vars. Numeric types are any of int, float, etc.
fld f("1");
int i;
i = f; // 1
i = f+1; // 2
f = 2; // "2"
f + " m/s" // "2 m/s"
f + 5 // "7"
SCC supports following AWK’s global variables:
$ - derived from std::deque<fld> — line’s fields
NF - long, number of fields (set after read_line())
NR - long, number of records (set after read_line())
OFS - strr, output field separator
FS - strr, input field separator.
ORS - strr, output record separator
RS - strr, input record separator.
FILENAME - const char[], current filename being processed
More examples. Sum-up DF(1) used-disk-space column. In AWK and SCC:
df | awk '{n+=$3}; END{print n}'
31399199
df | scc 'WRL n+=$3; n'
31399199
We can also replace column number with symbolic name (from df output header):
df | scc -H 'WRL n+=$("Used"); n'
31399199
Prepend line number to every line.
echo -e 'aaa\nbbb' | scc -p NR 1 aaa 2 bbb
For every line: first NR is printed (notice that there is no semicolon), then $0.
Now lets make comma separated fields out of colon separated. Option -o sets OFS (output field separator), -F - set FS Snippet is empty in this example.
echo 1:2:3 | scc -F: -o, -p 1,2,3
Or equivalent:
echo 1:2:3 | FS=: OFS=, scc -p 1,2,3
Replace "-" or "none" with "n/a" in 2nd column using std::regex. In AWK and SCC:
echo -e '1 -\n2 none\n3 abc' | awk '{gsub(/^(none|-)$/,"n/a",$2); print $0}'
1 n/a
3 n/a
4 abc
echo -e '1 -\n2 none\n3 abc' | scc -p 'if ($2 == "(none|-)"_R) $2="n/a";'
1 n/a
3 n/a
4 abc
C-string with _R suffix are std::regex literal. The operator== calls std::regex_match(). Unfortunately GCC’s LIBSTDC++ not yet have working std::regex_replace and std::regex_search and we have to use if.
git clone http://github.com/lvv/scc echo PATH+=":$PWD/scc" >> ~/.profile . ~/.profile
Current alpha version is v0.2.1, use "latest" git tag for relatively stable version. SCC uses C+11 extensively and some GCC extensions, so currently it can work only with GCC-4.7 (pre release). It might work with CLANG and MSVS, but this was not tested. For stream ops, version v0.2.* is about x100 times faster than v0.1 and several times faster than GAWK/MAWK. Boost is not used in v0.2. Regex in RS/FS are currently not supported (but were in v0.1)
C++ parser of semi-broken. To make multi-line scripts, as workaround use semicolon at the end of every line (even in comments).
If you need to use it with older GCC - use v0.1.