Re: Trouble implementing a standard library -- Calling C functions from within my language

alexfrunews@gmail.com
Wed, 19 Mar 2014 21:41:16 -0700 (PDT)

          From comp.compilers

Related articles
Trouble implementing a standard library -- Calling C functions from wi noitalmost@cox.net (noitalmost) (2014-03-19)
Re: Trouble implementing a standard library -- Calling C functions fro DrDiettrich1@aol.com (Hans-Peter Diettrich) (2014-03-20)
Re: Trouble implementing a standard library -- Calling C functions fro alexfrunews@gmail.com (2014-03-19)
Re: Trouble implementing a standard library -- Calling C functions fro noitalmost@cox.net (noitalmost) (2014-03-21)
| List of all articles for this month |
From: alexfrunews@gmail.com
Newsgroups: comp.compilers
Date: Wed, 19 Mar 2014 21:41:16 -0700 (PDT)
Organization: Compilers Central
References: 14-03-049
Injection-Date: Thu, 20 Mar 2014 04:41:17 +0000
Keywords: code
Posted-Date: 20 Mar 2014 10:30:18 EDT

On Wednesday, March 19, 2014 3:28:13 PM UTC-7, noitalmost wrote:
> I need some small-scale examples, like a toy compiler would do if it
> needed access to some libc functions.
>
> Let's say I have a module (library) called Std that provides
> procedure ln (x : float) float;
> procedure open(fname : string; mode : int) int;
> procedure read(fd : int; buf : ^byte; cnt : int) bool;
>
> On a Linux system, I want to just wrap the libc functions
> double log(double x);
> int open(const char *fname, int mode);
> ssize_t read(int fd, void *buf, int cnt);
>
> In prior projects, I always output assembly and just did system calls.
> This time it's different. First, I have an intermediate form that can
> produce C source code. Second, I want to be able to access functions
> (say from math.h) that aren't Linux system calls.
>
> I don't think the intermediate form matters for this discussion. It
> can be assumed that module Std gets parsed into an AST, and that I can
> walk the AST to generate C code.
>
> How is this usually handled? I could put a CCall node in my AST and have the
> parser recognize
>
> procedure ln (x : float) :
> return CCall( ln, x );
> end ln;
>
> And then have
>
> class CCall : public Expr
> {
> CCall(string name, vector<Expr*> params);
> string genC();
> ...
> }
>
> where, Expr is a AstNode. But CCall::genC() would be a really
> complicated list of special cases. Maybe I also need to pass the C
> return type and the C types of the parameters? And I'm on my way
> toward an Ada-style foreign-function interface. Eek! Isn't there a
> simpler way?


I'm not sure where exactly the problem lies, but here goes some thinking
w.r.t. C declarations and code generation...


In my C compiler I represent C types in a kind of AST that has them
deconvoluted and straightened out, e.g.:


int a -> a: int
int *a -> a: * int
int a[2][3] -> a: [2] [3] int
struct Tag* a[1] -> a: [1] * struct Tag
char (*a[2])(int b, int c[5]) -> a: [2] * (b: int, c: * int) char
etc


I pretty much literally store this declaration/type info in a list. I do store
the brackets and parens along with the tokens designating the fundamental
types and the other necessary tokens (array dimensions, identifiers, stars,
etc). These brackets and parens let me know what I'm dealing with (arrays or
functions) and since they're balanced I can dive into them (for example, to
get function parameter info) or skip them (if I don't care about function
params at the moment).


This unwound type/declaration representation can be naturally turned back into
C declarations, if one's interested in generating them for reference or
(re)compilation.


Arbitrary complexity is possible. Structures complicate things a little. I
keep structure declarations separate from declarations that use structures.
E.g.:


// refers to an empty struct tagged as Tag at first:
struct Tag* a[1] -> a: [1] * struct Tag


// now we define the struct:
struct Tag { int dummy; } -> <something>: struct Tag { dummy: int }


// and after this the type of a is completed and made
// to refer to the declaration of the struct Tag with
// the {} body instead of referring to the empty struct


You may want to generate or use such declarations if you want to:
- use your prototypes instead of those from #include <math.h>
- do some type parameter/return value type checking


But if you're translating your language into compilable C, you need more than
just invoking a function. Where do you actually take function parameters from?
They must be C expressions, containing constants, variables, other function
calls and operators.


Perhaps, you need to naturally generate C code out of all your expressions and
other building blocks of your source language? If each of them is already
parsed into a (sub)tree or a (sub)stack (e.g. containing the expression in the
Reverse Polish Notation, ready for evaluation or code generation), it's not
much of a problem and it's likely easier than doing the same for the purpose
of generating assembly code.


Alex


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.