How are strings freed in ATS2? - ats

http://www.ats-lang.org/Documents.html includes "Introduction to Programming in ATS", which includes the assertion that fileref_get_line_string returns a Strptr1 (a look in filebas.dats shows that it returns a String via strptr2string), and it includes this code:
#include "share/atspre_staload.hats"
#include "share/atspre_staload_libats_ML.hats"
implement main0() = loop() where
fun loop(): void = let
val isnot = fileref_isnot_eof(stdin_ref)
in
if isnot then let
val line = fileref_get_line_string(stdin_ref)
val () = print_string(line)
val () = strptr_free(line)
in
loop()
end else ()
end
end
Which throws a type error if the strptr_free line is included. If that line isn't included, the program blatantly leaks memory. Is there current documentation or are there ATS2 examples that show how the fileref_* words are supposed to be used? What is the ATS2 version of the code above?

There are two versions of fileref_get_line_string: one in prelude/filebas and
the other in libats/ML/filebas. For getting linear strings, you need
the former:
#include
"share/atspre_staload.hats"
implement
main0() = loop() where
fun
loop(): void = let
val
isnot =
fileref_isnot_eof(stdin_ref)
in
if isnot then let
val line =
fileref_get_line_string(stdin_ref)
val () =
print_strptr(line)
val () = free(line)
in
loop()
end else ()
end
end

Related

Trying to read MacOS clipboard contents

On my adventure to learn Rust I decided to try and print to the cli contents of the clipboard. I've done this before in Swift so thought I would have much issues in Rust.
However I'm having a hard time printing the contents of the returned NSArray. I've spent a few hours playing around with different functions but haven't made much progress.
The Swift code I have that works:
import Foundation
import AppKit
let pasteboard = NSPasteboard.general
func reload() -> [String]{
var clipboardItems: [String] = []
for element in pasteboard.pasteboardItems! {
if let str = element.string(forType: NSPasteboard.PasteboardType(rawValue: "public.utf8-plain-text")) {
clipboardItems.append(str)
}
}
return clipboardItems;
}
// Access the item in the clipboard
while true {
let firstClipboardItem = reload()
print(firstClipboardItem);
sleep(1);
}
Here is the Rust code:
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardReading, NSPasteboardTypeString};
use cocoa::foundation::NSArray;
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let changec = pid.changeCount();
let pid_item = pid.pasteboardItems();
if pid_item.count() != 0 {
let items = &*pid_item.objectAtIndex(0);
println!("{:?}", items);
}
println!("{:?}", *pid.stringForType(NSPasteboardTypeString));
}
}
The code above produces: *<NSPasteboardItem: 0x6000021a3de0>*
EDIT:
I've made a little progress but stuck on one last bit. I've managed to get the first UTF8 char out of the clipboard.
The issue I have is if I copy the text: World the system will loop the correct amount of times for the word length but will only print the first letter, in this case W. Output below:
TEXT 'W'
TEXT 'W'
TEXT 'W'
TEXT 'W'
TEXT 'W'
The bit I'm trying to get my head around is how to move to the next i8. I can't seem to find a way to point to the next i8.
The NSString function UTF8String() returns *const i8. I'm scratching my head with how one would walk the text.
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardTypeString};
use cocoa::foundation::{NSArray, NSString};
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let changec = pid.changeCount();
let nsarray_ptr = pid.pasteboardItems();
if nsarray_ptr.count() != 0 {
for i in 0..NSArray::count(nsarray_ptr) {
let raw_item_ptr = NSArray::objectAtIndex(nsarray_ptr, i);
let itm = raw_item_ptr.stringForType(NSPasteboardTypeString);
for u in 0..itm.len() {
let stri = itm.UTF8String();
println!("TEXT {:?}", *stri as u8 as char);
}
}
}
}
}
To everyone who's looked/commented on this so far thank you.
After reading some tests provided by cocoa I figured out what I needed to do.
The code below prints the contents of the clipboard. Thanks to those who pointed me in the right direction.
use cocoa::appkit::{NSApp, NSPasteboard, NSPasteboardTypeString};
use cocoa::foundation::{NSArray, NSString};
use std::{str, slice};
fn main() {
unsafe {
let app = NSApp();
let pid = NSPasteboard::generalPasteboard(app);
let nsarray_ptr = pid.pasteboardItems();
if nsarray_ptr.count() != 0 {
for i in 0..NSArray::count(nsarray_ptr) {
let raw_item_ptr = NSArray::objectAtIndex(nsarray_ptr, i);
let itm = raw_item_ptr.stringForType(NSPasteboardTypeString);
let stri = itm.UTF8String() as *const u8;
let clipboard = str::from_utf8(slice::from_raw_parts(stri, itm.len()))
.unwrap();
println!("{}", clipboard);
}
}
}
}

Insert into hashmap in a loop

I'm opening a CSV file and reading it using BufReader and splitting each line into a vector. Then I try to insert or update the count in a HashMap using a specific column as key.
let mut map: HashMap<&str, i32> = HashMap::new();
let reader = BufReader::new(input_file);
for line in reader.lines() {
let s = line.unwrap().to_string();
let tokens: Vec<&str> = s.split(&d).collect(); // <-- `s` does not live long enough
if tokens.len() > c {
println!("{}", tokens[c]);
let count = map.entry(tokens[c].to_string()).or_insert(0);
*count += 1;
}
}
The compiler kindly tells me s is shortlived. Storing from inside a loop a borrowed value to container in outer scope? suggests "owning" the string, so I tried to change
let count = map.entry(tokens[c]).or_insert(0);
to
let count = map.entry(tokens[c].to_string()).or_insert(0);
but I get the error
expected `&str`, found struct `std::string::String`
help: consider borrowing here: `&tokens[c].to_string()`
When I prepend ampersand (&) the error is
creates a temporary which is freed while still in use
note: consider using a `let` binding to create a longer lived
There is some deficiency in my Rust knowledge about borrowing. How can I make the hashmap own the string passed as key?
The easiest way for this to work is for your map to own the keys. This means that you must change its type from HasMap<&str, i32> (which borrows the keys) to HashMap<String, i32>. At which point you can call to_string to convert your tokens into owned strings:
let mut map: HashMap<String, i32> = HashMap::new();
let reader = BufReader::new(input_file);
for line in reader.lines() {
let s = line.unwrap().to_string();
let tokens:Vec<&str> = s.split(&d).collect();
if tokens.len() > c {
println!("{}", tokens[c]);
let count = map.entry(tokens[c].to_string()).or_insert(0);
*count += 1;
}
}
Note however that this means that tokens[c] will be duplicated even if it was already present in the map. You can avoid the extra duplication by trying to modify the counter with get_mut first, but this requires two lookups when the key is missing:
let mut map: HashMap<String, i32> = HashMap::new();
let reader = BufReader::new(input_file);
for line in reader.lines() {
let s = line.unwrap().to_string();
let tokens:Vec<&str> = s.split(&d).collect();
if tokens.len() > c {
println!("{}", tokens[c]);
if let Some (count) = map.get_mut (tokens[c]) {
*count += 1;
} else {
map.insert (tokens[c].to_string(), 1);
}
}
}
I don't know of a solution that would only copy the key when there was no previous entry but still do a single lookup.

How to read user input in ATS?

In my ATS application, I am trying to read a input string from a user.
Is there any function in ATS that performs similar functionality as scanf function in C.. If not how to get the input from user without integrating ATS with JS or HTML.
Here is a simple way to read from STDIN:
#include
"share/atspre_staload.hats"
#include
"share/HATS/atspre_staload_libats_ML.hats"
implement
main0() =
{
//
val
lines =
streamize_fileref_line(stdin_ref)
//
val () = lines.foreach()(lam x => println! (x))
//
} (* end of [main0] *)
If you compile to C, then scanf is available. Here is a simple example:
#include
"share/atspre_staload.hats"
#staload
"libats/libc/SATS/stdio.sats"
implement
main0() =
{
//
var str1 = #[char][1024]()
var str2 = #[char][1024]()
//
val () = println! ("Enter name: ")
val ec = $extfcall(int, "scanf", "%s", addr#str1)
val () = assertloc (ec != 0)
val str1 = $UNSAFE.cast{string}(addr#str1)
//
val () = println! ("Enter your website name: ")
val ec = $extfcall(int, "scanf", "%s", addr#str2)
val () = assertloc (ec != 0)
val str2 = $UNSAFE.cast{string}(addr#str2)
//
val () = println! ("str1 = ", str1)
val () = println! ("str2 = ", str2)
//
}

How to construct a loop in ATS over a given string?

For instance, how can I write code in ATS that traverses a given string as is done by the following C code:
while ((c = *str++) != 0) do_something(c);
Well, there is always a combinator-based solution:
(str).foreach()(lam(c) => do_something(c))
The following solution is easy, accessible and doesn't require any unsafe features (but it does use one advanced feature: indexed string type).
fun
loop {n:int}(p0: string(n)): void =
if string_isnot_empty (p0) then let
val c = (g0ofg1)(string_head(p0))
val p0 = string_tail(p0)
in
do_something(c); loop(p0)
end
Full code: https://glot.io/snippets/ejpwxk2xzx
The following solution makes use of UNSAFE but it should be really easy to access:
staload
UNSAFE =
"prelude/SATS/unsafe.sats"
fun
loop(p0: ptr): void = let
val c = $UNSAFE.ptr_get<char>p0)
in
if isneqz(c) then (do_something(c); loop(ptr_succ<char>(p0)) else ()
end
val () = loop(string2ptr(str))

How to turn a floating point number into a string in ATS?

Basically, I am looking for a function of the following interface:
fun double2string(x: double): string
which converts a double into a string representation for it. For instance, double2string(3.14) should return "3.14".
Sometimes I choose following cheat:
#include "share/atspre_define.hats"
#include "share/atspre_staload.hats"
%{^
#include <assert.h>
char *double2string(double x) {
#define DECIMAL 8
#define DECIMAL_FORMAT "%.8e"
#define DECIMAL_LEN DECIMAL+2+5
char *s = malloc(DECIMAL_LEN+1);
assert(NULL != s);
memset(s, 0, DECIMAL_LEN+1);
snprintf(s, DECIMAL_LEN, DECIMAL_FORMAT, x);
return s;
}
%}
extern fun double2string (x: double): strptr = "mac#"
implement main0 () = {
val s = double2string 1234567890.1234567890
val () = println! s
val () = free s
}
There is a function of the name atspre_string_make_snprintf in ATSLIB for constructing strings. For instance, one can do:
#include
"share/atspre_staload.hats"
fun
double2string(x: double): string =
$extfcall
(
string, "atspre_string_make_snprintf", "%.8e", x
) (* double2string *)
implement
main0() =
println! ("Pi = ", double2string(3.1415926535))
What is returned by atspre_string_make_snprintf is a linear string (strptr), which can be freed:
fun double2strptr(x: double): Strptr1 = $extfcall(...)
When compiling, please remember to pass the flag -latslib. You can try this example on-line:
https://glot.io/snippets/ejm6ous1fh
For compiling to JavaScript, 'String' can be called to do this. Actually, 'String' turns any given object into some form string representation for it.
Here is a quick way to do it:
fun
double2string
(
x0: double
) : string = let
val i0 = g0float2int_double_int(x0)
val d0 = g0float2int_double_int(100000000 * (x0 - i0))
val i0_rep = g0int2string(i0)
val d0_rep = g0int2string(d0)
val x0_rep =
string0_append3
($UNSAFE.strptr2string(i0_rep), ".", $UNSAFE.strptr2string(d0_rep))
// end of [val]
val ((*freed*)) = strptr_free(i0_rep)
val ((*freed*)) = strptr_free(d0_rep)
in
strptr2string(x0_rep)
end // end of [double2string]

Resources