How do you read in and access arguments from a main function in ATS2? - ats

This is a common, basic task, so it would be good to know an appropriate way to do this. A similar program in C++ might look like this (ref):
#include <iostream>
using namespace std;
int main(int argc, char** argv)
{
for (int i = 0; i < argc; ++i)
cout << argv[i] << "\n";
return 0;
}
In this example, we are printing out each of the arguments on the command line. So that running the programming like ./main.exe asdf 1234 bob would give:
./main.exe
asdf
1234
bob

This is very similar to the same kind of program in C, with a few differences related to constraints and linear types. The constraints are straightforward once set up:
(*
Run patscc -o hello hello.dats
*)
#include "share/atspre_staload.hats"
implement main0{n}
(argc, argv): void = {
fun echoArgs{i:nat | i < n}
(ii: int(i), argv: !argv(n)): void = {
val () = println!("arg ", ii, " is ", argv[ii])
val () = if ii + 1 < argc then echoArgs(ii + 1, argv)
}
val () = echoArgs(0, argv)
}
Since we need to access the contents of argv, we have to change the view of its viewtype by supplying the linear-dependent type !argv(n); the ! corresponds to the bang in linear logic indicating that values of that type are still available after the function call; the (n) just means argv is a string array of size n. We have to guarantee the index i into the array is less than the size n of the array.

Here is a combinator-based style:
#include "share/atspre_staload.hats"
#include "share/atspre_staload_libats_ML.hats"
implement
main0(argc, argv) = let
//
val args = listize_argc_argv(argc, argv)
//
in
list0_foreach(args, lam(arg) => println!(arg))
end // end of [main0]

Probably, the more common desire is to get flags and arguments from the command line, to change program behavior. For this, you can just use getopt (or with slightly more complexity but a much nicer CLI interface, GNU getopt). These functions do basically everything for you.
For a somewhat silly example:
#include "share/atspre_staload.hats"
%{^
#include <unistd.h>
%}
extern fun getopt{n:int}(argc: int, argv: !argv(n), flags: string): int = "mac#"
val trad = ref<bool>(false)
val color = ref<bool>(false)
val progname = ref<string>("hello4")
fn get_progname{n:int}(argc: int(n), argv: !argv(n)): void =
if argc > 0 then
!progname := argv[0]
implement main0(argc, argv) = (
get_progname(argc, argv);
process_args(argc, argv);
case+ (!trad, !color) of
| (true, true) => println!("\033[31;1mhello world\033[0m")
| (true, false) => println!("hello world")
| (false, true) => println!("\033[31;1mHello, world!\033[0m")
| (false, false) => println!("Hello, modern world!")
) where {
fn usage(): void = (
fprintln!(stderr_ref, "usage: ", !progname, " [-htc]");
exit(1);
)
fun process_args{n:int}(argc: int, argv: !argv(n)): void =
let
val r = getopt(argc, argv, "htc")
in
if r >= 0 then (
ifcase
| r = 'h' => usage()
| r = 't' => (!trad := true; process_args(argc, argv))
| r = 'c' => (!color := true; process_args(argc, argv))
| _ => (println!("fell through with: ", $UNSAFE.cast{char}(r)); usage())
)
end
}
Usage:
$ ./hello4
Hello, modern world!
$ ./hello4 -t
hello world
$ ./hello4 -c
Hello, world! <-- this is red

Related

How to use spirit X3 parse into a class with constructor containing parameters?

I am a new man on using spirit x3, I read some document from official site or from other github repositories. But, I can not find how to parse into a class with parameters. I referred to the former question: Boost-Spirit (X3) parsing without default constructors
I wrote a sample to test it, I will present my codes in the following area. My pain is how to use x3::_attr, and how to pass parsed parameters to the class constructor?
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
struct MyPair {
MyPair(int x, int y) : mx(x), my(y) {};
int mx;
int my;
};
class MyDu {
public:
MyDu() {};
MyDu(int x, int y) : mx(x), my(y) {};
int mx;
int my;
};
int main()
{
namespace x3 = boost::spirit::x3;
using x3::int_;
std::vector<MyPair> pairs;
MyDu myDu;
char const *first = "11:22", *last = first + std::strlen(first);
//auto pair = x3::rule<struct pair_, std::vector<MyPair> >{}
// = (int_ >> ':' >> int_)
// [([&](auto& ctx) {
// auto& attr = x3::_attr(ctx);
// using boost::fusion::at_c;
// return x3::_val(ctx).emplace_back(at_c<0>(attr), at_c<1>(attr));
// })]
//;
auto pair = x3::rule<class MyDu_, MyDu >{}
= (int_ >> ':' >> int_)
[([&](auto& ctx) {
auto& attr = x3::_attr(ctx);
using boost::fusion::at_c;
//return x3::_val(ctx)(at_c<0>(attr), at_c<1>(attr));
ctx = MyDu(at_c<0>(attr), at_c<1>(attr));
return x3::_val(ctx);
})]
;
//bool parsed_some = parse(first, last, pair % ',', pairs);
bool parsed_some = parse(first, last, pair, myDu);
if (parsed_some) {
std::cout << "Parsed the following pairs" << std::endl;
//for (auto& p : pairs) {
// std::cout << p.mx << ":" << p.my << std::endl;
//}
std::cout<<myDu.mx<<","<<myDu.my<<std::endl;
}
system("pause");
}
Any one who can fix my error, and parse into a class in my code ? Thanks!
Perhaps you were missing the way to assign to the rule's value using _val:
Live On Coliru
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>
struct MyDu {
MyDu(int x, int y) : mx(x), my(y){};
int mx;
int my;
};
int main() {
namespace x3 = boost::spirit::x3;
using x3::int_;
MyDu myDu{1,2};
std::string const s = "11:22";
auto assign = [](auto& ctx) {
using boost::fusion::at_c;
auto& attr = x3::_attr(ctx);
x3::_val(ctx) = MyDu(at_c<0>(attr), at_c<1>(attr));
};
auto pair = x3::rule<class MyDu_, MyDu>{} = (int_ >> ':' >> int_)[assign];
if (parse(begin(s), end(s), pair, myDu)) {
std::cout << "Parsed: " << myDu.mx << ", " << myDu.my << "\n";
}
}
Prints
Parsed: 11, 22
Oh, fantastic! Many thanks, sehe, you help me solve the problem bothering me for some while.
In fact I can not find document on spirit how to use attr, i only find a doc from "Ruben-Van-Boxem-Parsing-CSS-in-C-with-Boost-Spirit-X3",
_val :A reference to the attribute of the innermost rule invoking _where :the parser Iterator range to the input stream
_attr : A reference to the a˛ribute of the parser
_pass: A reference to a bool flag that can be used to force the parser to fail
could you share some info on these parameters. Many thanks again!

Nim argv equivalent

I'm interfacing a C utility with Nim. The C util main function expects argc and argv. I can get argc from paramCount but how do I get a C compatible list of args for argv?
int main (int argc, char **argv);
proc main*(argc: cint; argv: cstringArray): cint
Apart from paramCount() there is the function paramStr() which
actually gets the argument strings.
If your C program is:
int cmain(int argc, char **argv)
{
int x;
for (x=0; x < argc; x++)
printf("%d: %s\n", x, argv[x]);
}
Then your Nim program test.nim, needs to convert the values returned by
the repeated paramStr() invocations to a compatible char **:
{.compile: "cmain.c".}
proc cmain(c: cint, a: cstringArray): cint {.importc.}
import os
var nargv = newSeq[string](paramCount())
var x = 0
while x < paramCount():
nargv[x] = paramStr(x+1) # first is program name
x += 1
var argv: cStringArray = nargv.allocCStringArray()
discard cmain(cint(paramCount()), argv)
argv.deallocCStringArray()
Then running nim c -r test.nim def ghi gives:
0: def
1: ghi
(using the compile pragma you don't need to make a shared object from your C file, and link against that, but of course that is possible as well)
You have your seq of strings variable:
var data: seq[string] = #["A", "B", "C", "D"]
And you allocate it before sending it to the C function:
var cdata: cstringArray = data.allocCStringArray()
main(5, cdata)
cdata.deallocCStringArray()

Last element of a for loop

I'm splitting a document by a string delimiter in C++.
This is a minimal Python code to demonstrate the problem. la is splitted by 'x' to get (a,b,b) and (c,d) (only the element between x, or between x and end of file is recorded)
la = ['a','x','a','b','b','x','c','d']
out = []
tmp = []
inside = False
for a in la:
if a == "x":
if inside:
out.append(tmp)
tmp = []
inside = True
continue
if inside:
tmp.append(a)
out.append(tmp)
for a in out:
print a
There is code duplication here for the last element out.append(tmp). How do I move it inside the loop?
(out.append(tmp) is actually some large code and it's prone to error to write in different places).
P/S: Since the actual code is in C++, no special function from python is allowed to call in solving the problem
A minimal C++ code, I'm reading from a stringstream:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
// your code goes here
stringstream instream("a x b c d x c d");
vector<string> result;
string word, content;
while(getline(instream, word, ' ')) {
if (word == "x") {
result.push_back(content);
content = "";
continue;
}
content += word;
}
return 0;
}
Not sure why you would not just append outside the loop but you can check the length in the loop to catch the end elements:
out = []
tmp = []
for ind, ele in enumerate(la):
if ele == "x":
if tmp:
out.append(tmp)
tmp = []
elif ind == len(la) - 1:
tmp.append(ele)
out.append(tmp)
else:
tmp.append(ele)
You can use range in place of enumerate.
If you want to use continue you can remove the else:
for ind, ele in enumerate(la):
if ele == "x":
if tmp:
out.append(tmp)
tmp = []
continue
elif ind == len(la) - 1:
out.append(tmp)
tmp.append(ele)
I have zero experience with c++ but using stringstream.eof to catch the end of file might to do what you want:
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using namespace std;
int main() {
// your code goes here
stringstream instream("x a x b c d x c d x");
vector<string> result;
string word, content;
while(true) {
getline(instream, word, ' ');
if (instream.eof()){
if (word != "x"){
content += word;
}
cout << content << "\n";
break;
}
if (word == "x") {
result.push_back(content);
cout << content << "\n";
content = "";
continue;
}
content += word;
}
return 0;
}
Output:
a
bcd
cd
You also need to handle the case where he first character is x where you would output an empty string

Main function with arguments

I'm trying to understand the main function with the arguments argc and argv. In the command line I am trying to copy the contents of multiple txt files on the screen (concatenation). When I write in the command line appname.exe something f1.txt, the content from the f1.txt prints in a loop. If f1.txt had the text "abcda" the output in console would be "abcdaabcdaabcda...". Sorry for my english; can someone help me understand what I did wrong?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main(int argc, char *argv[])
{
int i;
for (i = 2; i <= argc - 1;i+2)
{
FILE *f = fopen(argv[i], "r");
if (f == 0)
{
printf("Error\n");
}
else
{
int x;
while ((x = fgetc(f)) != EOF)
{
printf("%c", x);
}
}
fclose(f);
}
}
Here's one big problem:
for (i = 2; i <= argc - 1;i+2)
I think you mean to do:
for (i = 2; i <= argc - 1; i++)

How to perform a range-based c++11 for loop on char* argv[]?

I would like to try out c++11 range-based for loop on char* argv[] but I am getting errors. By current approach is :
for( char* c : argv )
{
printf("param: %s \n", c);
}
and in my makefile I have the following line :
g++ -c -g -std=c++11 -O2 file.cc
argv is an array of pointers to raw strings, you can't obtain a range from it directly.
With C++17 you can use std::string_view to avoid allocating strings:
for (auto && str : std::vector<std::string_view> { argv, argv + argc })
{
std::printf("%s\n", str.data()); // Be careful!
std::cout << str << std::endl; // Always fine
fmt::print("{}\n", str); // <3
}
Take caution when using string_view with printf because:
Unlike std::basic_string::data() and string literals, data() may return a pointer to a buffer that is not null-terminated.
argv always contains null-terminated strings so you're fine here though.
Without C++17 you can simply use std::string:
for (auto && str : std::vector<std::string> { argv, argv + argc })
std::printf("param: %s\n", str.c_str());
Starting from C++20 you can use std::span:
for (auto && str : std::span(argv, argc))
std::printf("param: %s\n", str);
You can't use the range-based loop since you don't have a range.
You can either write your own range wrapper (a kind of "array view"), or just use a normal loop:
for (char ** p = argv, e = argv + argc; p != e; ++p)
{
// use *p
}
With a wrapper:
#include <cstddef>
template <typename T>
struct array_view
{
T * first, * last;
array_view(T * a, std::size_t n) : first(a), last(a + n) {}
T * begin() const { return first; }
T * end() const { return last; }
};
template <typename T>
array_view<T> view_array(T * a, std::size_t n)
{
return array_view<T>(a, n);
}
Usage:
for (auto s : view_array(argv, argc))
{
std::cout << s << "\n";
}

Resources