All Projects → Hirrolot → Metalang99

Hirrolot / Metalang99

Licence: mit
A functional language for C99 preprocessor metaprogramming

Programming Languages

c
50402 projects - #5 most used programming language
cpp
1120 projects
cpp11
221 projects
macros
77 projects
metaprogramming
66 projects
c99
33 projects

Projects that are alternatives of or similar to Metalang99

Request Compose
Composable HTTP Client
Stars: ✭ 80 (-47.37%)
Mutual labels:  functional-programming, fp
Fs2
Compositional, streaming I/O library for Scala
Stars: ✭ 1,998 (+1214.47%)
Mutual labels:  functional-programming, fp
Zio Logging
Simple logging for ZIO apps, with correlation, context & pluggable backends out of the box.
Stars: ✭ 85 (-44.08%)
Mutual labels:  functional-programming, fp
Stm4cats
STM monad for cats-effect
Stars: ✭ 35 (-76.97%)
Mutual labels:  functional-programming, fp
Test State
Scala Test-State.
Stars: ✭ 119 (-21.71%)
Mutual labels:  functional-programming, fp
Funland
Type classes for interoperability of common algebraic structures in JavaScript, TypeScript and Flow
Stars: ✭ 46 (-69.74%)
Mutual labels:  functional-programming, fp
Doobie
Functional JDBC layer for Scala.
Stars: ✭ 1,910 (+1156.58%)
Mutual labels:  functional-programming, fp
Cfl
a Compileable statically typed Functional programming Language
Stars: ✭ 7 (-95.39%)
Mutual labels:  interpreter, functional-programming
Pointless
Pointless: a scripting language for learning and fun
Stars: ✭ 116 (-23.68%)
Mutual labels:  interpreter, functional-programming
Scalajs React
Facebook's React on Scala.JS
Stars: ✭ 1,524 (+902.63%)
Mutual labels:  functional-programming, fp
Flawless
WIP Delightful, purely functional testing no-framework. Don't even try to use it at work!
Stars: ✭ 33 (-78.29%)
Mutual labels:  functional-programming, fp
Sup
Composable, purely functional healthchecks in Scala.
Stars: ✭ 138 (-9.21%)
Mutual labels:  functional-programming, fp
Bugz
🐛 Composable User Agent Detection using Ramda
Stars: ✭ 15 (-90.13%)
Mutual labels:  functional-programming, fp
Rambda
Faster and smaller alternative to Ramda
Stars: ✭ 1,066 (+601.32%)
Mutual labels:  functional-programming, fp
Mappy
A functional programming language. Like LISP but focused around maps rather than lists.
Stars: ✭ 10 (-93.42%)
Mutual labels:  interpreter, functional-programming
Pattern Matching Ts
⚡ Pattern Matching in Typescript
Stars: ✭ 107 (-29.61%)
Mutual labels:  functional-programming, fp
Frameless
Expressive types for Spark.
Stars: ✭ 717 (+371.71%)
Mutual labels:  functional-programming, fp
D4s
Dynamo DB Database Done Scala-way
Stars: ✭ 27 (-82.24%)
Mutual labels:  functional-programming, fp
Clear Config
Scala FP configuration library with a focus on runtime clarity
Stars: ✭ 108 (-28.95%)
Mutual labels:  functional-programming, fp
Returns
Make your functions return something meaningful, typed, and safe!
Stars: ✭ 2,015 (+1225.66%)
Mutual labels:  functional-programming, fp

Metalang99

CI docs book spec

The dark side of the force is a pathway to many abilities, some considered to be unnatural.
    -- Darth Sidious

[ examples/demo.c ]

#include <metalang99.h>

// Compile-time list manipulation {
// 3, 3, 3, 3, 3
static int five_threes[] = {
    ML99_listEvalCommaSep(ML99_listReplicate(v(5), v(3))),
};

// 5, 4, 3, 2, 1
static int from_5_to_1[] = {
    ML99_listEvalCommaSep(ML99_listReverse(ML99_list(v(1, 2, 3, 4, 5)))),
};

// 9, 2, 5
static int lesser_than_10[] = {
    ML99_listEvalCommaSep(
        ML99_listFilter(ML99_appl(v(ML99_greater), v(10)), ML99_list(v(9, 2, 11, 13, 5)))),
};
// }

// Macro recursion {
#define factorial(n)        ML99_natMatch(n, v(factorial_))
#define factorial_Z_IMPL()  v(1)
#define factorial_S_IMPL(n) ML99_mul(ML99_inc(v(n)), factorial(v(n)))

ML99_ASSERT_EQ(factorial(v(4)), v(24));
// }

// Overloading on a number of arguments {
typedef struct {
    double width, height;
} Rect;

#define Rect_new(...) ML99_OVERLOAD(Rect_new_, __VA_ARGS__)
#define Rect_new_1(x)                                                                              \
    { x, x }
#define Rect_new_2(x, y)                                                                           \
    { x, y }

static Rect _7x8 = Rect_new(7, 8), _10x10 = Rect_new(10);
// }

// ... and more!

int main(void) {
    // Yeah. All is done at compile time.
}

(Hint: v(something) evaluates to something.)

Metalang99 is a functional language aimed at full-blown C99 preprocessor metaprogramming.

It features a wide range of concepts, including algebraic data types, control flow operators, collections, recursion, and auto-currying -- to develop both small and complex metaprograms painlessly.

Table of contents

Motivation

Macros are used to abstract away frequently occurring syntactical structures, they are the building material that lets you write your application in a language of a problem domain. However, metaprogramming in C is utterly castrated: we cannot even operate with control flow, integers, unbounded sequences, and compound data structures, thereby throwing a lot of hypothetically useful metaprograms out of scope.

To solve the problem, I have implemented Metalang99. Having its functionality at our disposal, it becomes possible to develop even fairly non-trivial metaprograms, such as Datatype99:

#include <datatype99.h>

datatype(
    BinaryTree,
    (Leaf, int),
    (Node, BinaryTree *, int, BinaryTree *)
);

int sum(const BinaryTree *tree) {
    match(*tree) {
        of(Leaf, x) return *x;
        of(Node, lhs, x, rhs) return sum(*lhs) + *x + sum(*rhs);
    }
}

As you can see, advanced metaprogramming with Metalang99 allows to drastically improve quality of your code -- make it safer, cleaner, and more maintainable.

Getting started

  1. Download this repository.
  2. Add metalang99/include to your include paths.
  3. #include <metalang99.h> beforehand (or use separate headers described in the docs).

PLEASE, use Metalang99 only with -ftrack-macro-expansion=0 (GCC) or something similar, otherwise it will throw your compiler to the moon. Precompiled headers are also very helpful.

Tutorial | Examples | Etudes | User documentation

Happy hacking!

Prominent aspects

  • Macro recursion. Recursive calls behave as expected. In particular, to implement recursion, Boost/Preprocessor just copy-pastes all recursive functions up to a certain limit and forces to either keep track of recursion depth or rely on a built-in deduction; Metalang99 is free from such drawbacks.

  • Almost the same syntax. Metalang99 does not look too alien in comparison with Order PP because the syntax differs insignificantly from usual preprocessor code.

  • Partial application. Tracking auxiliary arguments here and there results in code clutter; partial application, in turn, allows to naturally capture an environment by applying your constant values first. Besides that, partial application facilitates better reuse of metafunctions.

  • Debugging and error reporting. You can conveniently debug your macros with ML99_abort and report fatal errors with ML99_fatal. The interpreter will immediately finish its work and do the trick.

Philosophy and origins

My work on Poica, a research programming language implemented upon Boost/Preprocessor, has left me unsatisfied with the result. The fundamental downsides of Boost/Preprocessor made themselves felt: macro blueprinting was a really hard-to-debug disaster, especially in the case of higher-order metafunctions, and the absence of partial application forced me to reify the same patterns into macros each time. The code base got simply unmaintainable.

After I realised that the metaprogramming framework lacks abstractions, I started to implement Metalang99. Honestly, it turned out to be a much tougher and fascinating challenge than I expected -- it took half of a year of hard work to release v0.1.0. As a real-world application of Metalang99, I created Datatype99 exactly of the same form I wanted it to be: the implementation is highly declarative, the syntax is nifty, and the semantics is well-defined.

Finally, I want to say that Metalang99 is only about syntactic transformations and not about CPU-bound tasks; the preprocessor is just too slow and limited for such kind of abuse.

Contributing

See CONTRIBUTING.md.

At this moment, contributions that optimise the interpreter and the standard library are highly appreciated.

Architecture

See ARCHITECTURE.md.

Optimisation guide

Generally speaking, the fewer reduction steps you perform, the faster you become. A reduction step is a concept formally defined in the specification. Here's its informal (and imprecise) description:

  • Every v(...) is a reduction step.
  • Every ML99_call(op, ...) induces as many reduction steps as required to evaluate op and ... plus 1.

To perform fewer reduction steps, you can:

  • Use ML99_callUneval,
  • Use the plain versions (e.g., ML99_CONSUME instead of ML99_consume),
  • Call a macro as <X>_IMPL(...), provided that all the arguments are evaluated and macro blueprinting will not happen. I strongly recommend to use this trick only if X is placed locally to a caller in order to ensure the correctness of expansion.

FAQ

Q: What about compile-time errors?

A: Metalang99 detects and reports about syntactic errors, where possible. For example (-E flag):

// !"Metalang99 syntax error": `123`
ML99_EVAL(123)

However, compile-time errors can be still quite obscured. I strongly recommend using -ftrack-macro-expansion=0 (GCC) as it tells a compiler to not print a useless bedsheet of macro expansions.

Q: What about debugging?

A: See the chapter Testing, debugging, and error reporting.

Q: Why don't you use third-party code generators?

A:

  • Preprocessor macros are far more seamlessly integrated with a code base: you can invoke them in the same source files where ordinary code in C is written.
  • IDE support.
  • Avoid additional burden with distribution and setup of third-party code generators.

Q: Compilation times?

A: To run the benchmarks, execute ./scripts/bench.sh from the root directory.

Q: Why formal specification?

A:

  • Formal proofs. With a mathematical model it becomes possible to prove things about Metalang99 formally; for example, the progress theorem, which can be stated as "the interpreter always knows what to do next".

  • It guides the implementation. The implementation gets adjusted with the specification (i.e. reflects the formal syntax and semantics), thereby making itself easier to reason about.

  • It guides the tests. We immediately see many, if not all corner cases, which are ought to be tested.

  • Distinctness. It is much easier to answer questions like "Is it a bug of the implementation or it is a valid behaviour according to the specification?".

That is, the development flow is "specification-driven", if you prefer.

Q: Is Metalang99 Turing-complete?

A: Nope. The C/C++ preprocessor is capable to iterate only up to a certain limit (see this SO question). For Metalang99, this limit is defined in terms of reductions steps (see the specification).

Q: Why do we need powerful preprocessor macros in the presence of templates?

A: Metalang99 is primarily targeted at pure C, and C lacks templates. But anyway, you can find the argumentation for C++ at the website of Boost/Preprocessor.

Q: What standards are supported?

A: C99/C++11 and onwards.

Q: Why not generate an amalgamated header?

A: I don't like amalgamated headers because they induce burden with updating. In contrast to this, you can just add Metalang99 as a Git submodule and update it with git submodule update --remote.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].