zetzit / zz

πŸΊπŸ™ ZetZ a zymbolic verifier and tranzpiler to bare metal C
MIT License
1.6k stars 52 forks source link

ifdef with c defines #87

Closed aep closed 4 years ago

aep commented 4 years ago

the current preprocessor is bad and needs to be replaced. the design goals are:

so far this is the syntax i came up with:

using <stdio.h>::{printf};
using err;
using log;
using string;

#if defined(__WIN32__)
using <windows.h>

const char * os = 
#if defined(__linux__)
"linux"
#else
"who knows"
;

export fn main() -> int

#if defined(__APPLE__)
{
    log::info("hello unknown \n");
    return 0;
}
#elif defined(__linux__)
{
    log::info("hello linux\n");
    return 0;
}
#else
{
    log::info("hello linux\n");
    return 0;
}

struct Bob
#if defined (WITH_SOMETHING)
{
  int something;
}
#else
{

}

@jwerle thoughts?

jwerle commented 4 years ago

yeah this looks good to me. I've been use branching logic for macros in external headers. It will be great to be able to do it in ZZ

aep commented 4 years ago

alternative syntax using structured syntax instead of line based

note the bad indentation is intentional to show valid positions



using <stdio.h>::{printf};
using err;
using log;
using string;

if (defined(__WIN32__) {
  using <windows.h>
}

const char * os = if (defined(__WIN32__) {
   "linux"
} else {
    "who knows"
};

export fn main() -> int if defined(__APPLE__) {
    log::info("hello apple \n");
    return 0;
} else {
    log::info("hello linux\n");
    return 0;
}

struct Bob if defined (WITH_SOMETHING)
{
  int something;
} else {
}
jwerle commented 4 years ago

hmm i definitely see the appeal for a structured syntax like that. How are PP values defined here?

aep commented 4 years ago

even better:

using a generic if in declaration position and a # to indicate raw CPP expression

if #defined(__WIN32__) {
    using <windows.h>;
}

export fn main() -> int
if #defined(__APPLE__)
{
    log::info("hello apple \n");
    return 0;
}
else if #defined(__linux__)
{
    log::info("hello linux\n");
    return 0;
}
else
{
    log::info("hello dunno\n");
    return 0;
}

allows to expand the allowed expression to other stages like static values

struct B if static(1 == 0) {
    int never;
} else {
    int cool;
}

and macro invocations

fn display if @pkgconfig(have_gtk) {
}

also answers your question "How are PP values defined here?" they are not, but you can just use a const in static expressions

const bool USE_UI  = @pkgconfig(gtk) || @pkgconfig(qt)
fn display if static(USE_UI) {
}
jwerle commented 4 years ago

nice :)

aep commented 4 years ago

2 minor issues

the declaration expression is scoped before the function signature, so this is confusing but valid:

const int x = 3
fn display(int x) -> int where x > 2 if x > 2 {

} else {

}

this is impossible, but the syntax suggests its allowed:

fn display() if #something > 2 {} else {}

this could be solved by emitting the entire expression to CPP instead of solving it, but the static expression solver in CPP is extremely weak.

you can emit it into CPP explicitly like this:

fn display() if #(something > 2) {} else {}

so i think its ok to throw a solver error on the first expression, but this is a potential noobie trap, because the solution is non obvious.

so to avoid that i think the syntax should always require ( ) around CPP so that it is always clear where the CPP scope is

fn display() if #(something) {} else {}
jwerle commented 4 years ago

oh yeah, that took a second read too. yeah requiring () isn't terrible. It appears more readable