Combining ATS and C


When programming in ATS, a programmer may need to write C code occassionally. This is particularly common when the programmer does systems programming. Given that both ATS and C use the same data representation, it is largely straightforward for ATS and C to interact with each other. In particular, there is no need to write "wrappers" that are used in languages like Objective Caml or Haskell. In ATS, the syntax for enclosing C code is

%{ (some C code) %}
The symbol %{ can be replaced with either %{^ or %{$. The former and the latter mean that the enclosed C code needs to be put at the top and the bottom of the generated C code, respectively.

For a function to be called in C, we need to assign a global name to this function. For instance, the following syntax declares a function foo that takes a pair of integers and returns a boolean. This function is given a global name "ats_foo", which can be used in C code to refer to foo.

fun foo : (int, int) -> bool = "ats_foo"
We may implement foo either in ATS as follows:
implement foo (x, y) = ...
or in C as follows:
%{

ats_bool_type ats_foo (ats_int_type x, ats_int_type y) { ... }

%}
Note that the C types ats_int_type and ats_bool_type are defined in the file ccomp/runtime/ats_types.h

The following example involves a call in ATS to a function that is implemented in C:

// This function computes Fibonacci numbers
extern fun fibonacci (n: Nat): Nat = "fibonacci"

%{

ats_int_type fibonacci (ats_int_type n) {
  int res1, res2, tmp ;

  if (n < 1) return 0 ;
 
  res1 = 0 ; res2 = 1 ;
  while (n > 1) {
    --n ; tmp = res2 ; res2 += res1 ; res1 = tmp ;
  }

  return res2 ;
}

%}

fn fibonacci_usage (cmd: string): void =
  prerrf ("Usage: %s [integer]\n", @(cmd)) // print an error message

implement main (argc, argv) =
  if argc >= 2 then let
    val n = int1_of argv.[1] // turning string into integer
    val () = assert_errmsg
      (n >= 0, "The integer argument needs to be nonnegative.\n")
    val res = fibonacci n
  in
    printf ("fibonacci (%i) = %i\n", @(n, res))
  end else begin
    fibonacci_usage argv.[0]; exit {void} (1)
  end

It is also possible to assign names to types and values in ATS, and such names can be used in extenal C code. As an example, the type Tree in the following code is given a name exTree, which can be used in C code to refer to the type Tree.

dataviewtype tree (int) =
  Nil(0) | {n1,n2:two} Node(1) of (tree n1, int, tree n2)

viewtypedef Tree = [n:two] tree n

// assigning an external name to [Tree]
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
extern typedef "exTree" = Tree
extern fun ref_tree : Tree -> ref Tree = "ref_tree"

%{

ats_ref_type ref_tree (ats_ptr_type t) {
  exTree* r ;
  r = ats_malloc_gc (sizeof(exTree)) ;
  *r = (exTree)t ;
  return r ;
}

%}

The syntax for assigning a name to a value in ATS is given as follow:
extern val $name = $exp
where $name is a string literal (representing a valid identifier in C) and $exp ranges over dynamic expressions in ATS. When used in external C code, the string $name refers to the value of the expression $exp.

In the other direction, we can use $extype "SomeType" in ATS to refer to a type of the name SomeType in C. Similarly, we can use $extval ($typ, "SomeValue") for a value of the name SomeValue in C, where $typ is the supposed type of this value in ATS. For instance, stdout in ATS is defined as follows:

#define stdout $exval ($extype "ats_ptr_type", "stdout")
where ats_ptr_type is an alias for the type void* in C.

The code used for illustration is available here.