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) endIt 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 = $expwhere $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.