• Stars
    star
    106
  • Rank 325,871 (Top 7 %)
  • Language
    OCaml
  • Created over 5 years ago
  • Updated over 5 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Template-based macros for Reason/OCaml

reason-macros

Template-based macros for Reason/OCaml!

Try it out for yourself

https://astexplorer-macros.surge.sh , switch the language to "Reason" and toggle the "Transform" to on.

Basic examples

let%macro.toplevel ionicon = (name: capIdent, iconName: capIdent) => {
  [%str // toplevel
    module Eval__name = {
      let name = "$eval{name}";
      [@bs.module] [@react.component]
      external make:
        (
          ~className: string=?,
          ~fontSize: string=?,
          ~color: string=?,
          ~onClick: 'event => unit=?
        ) =>
        React.element =
        "react-ionicons/lib/$eval{iconName}";
    }
  ];
};

[%%ionicon (Link, IosLink)];
[%%ionicon (Download, MdDownload)];

becomes

module Link = {
  let name = "Link";
  [@react.component]
  external make:
    (
      ~className: string=?,
      ~fontSize: string=?,
      ~color: string=?,
      ~onClick: 'event => unit=?
    ) =>
    React.element =
    "react-ionicons/lib/IosLink"
};
module Download = {
  let name = "Download";
  [@react.component]
  external make:
    (
      ~className: string=?,
      ~fontSize: string=?,
      ~color: string=?,
      ~onClick: 'event => unit=?
    ) =>
    React.element =
    "react-ionicons/lib/MdDownload"
};
let%macro.let opt = (pattern, value, continuation) =>
  switch (eval__value) {
  | None => None
  | Some(eval__pattern) => eval__continuation
  };

let doSomethingWithOptionals = () => {
  let%opt who = Some("world");
  let%opt greeting = Some("Hello");
  Some(greeting ++ "" ++ who);
};

becomes

let doSomethingWithOptionals = () =>
  switch (Some("world")) {
  | None => None
  | Some(who) =>
    switch (Some("Hello")) {
    | None => None
    | Some(greeting) => Some(greeting ++ "" ++ who)
    }
  };
let%macro.let async = (pattern, value, continuation) => {
  Js.Promise.then_(
    eval__pattern => eval__continuation,
    eval__value
  );
};

let doSomethingAsync = () => {
  let%async text = fetch("Hello");
  let%async more = text->json;
  Js.Promise.resolve(more);
};

becomes

let doSomethingAsync = () =>
  Js.Promise.then_(
    text => Js.Promise.then_(more => Js.Promise.resolve(more), json(text)),
    fetch("Hello"),
  );

Installation

Bucklescript

npm i reason-macros-bin

  "ppx-flags": ["reason-macros-bin/ppx.js"]

esy + dune

add to package.json / esy.json "reason-macros": "git+https://github.com/jaredly/reason-macros"

and then in a dune file

  (preprocess (pps macros.ppx))

!IMPORTANT! The rest of this Readme is very disorganized and wrong.

User-defined macros. Without ppxs.

let%macro.fn something = (one, two, three) => {
  one + two + three
};

[%macro something(5, 6, 7)]

// I would like to support spreads (rest arguments).

// Also, macro-ify a record literal
// Or a js object literal

// Allow special things like [%FILENAME] and [%LINENO] and stuff
// Also would like a way to include a representation of the expression as text or something.

let%macro.switch thing = (value, cases) => {
  // hrmmm
  // is there a way to allow pseudo-procedures?
  // not suer.
  // Like going through each case and doing something to it.
  [%map cases(({pattern, condition, body}) => {
    [%case (pattern, condition, body)]
  })]
}


let%macro.let opt = (pattern, value, continuation) => {
  switch value {
    | Some(pattern) => continuation
    | None => None
  }
};

let%macro.let async = (pattern, value, continuation) => {
  Js.Promise.then_(value, pattern => continuation)
};

let%macro.async awesome = 10;

  • Simple macros
    • List of magics
      • for all macros

      • for decorator macros

        • payload is the thing we're attached to. Will have to get fancy for local bindings, how to include just the binding and not the continuation

        • for a binding

          • payload.binding.name will give you the name as an identifier token

            • will throw exception if it's attached to not-a-binding
          • payload.binding.value will give you the stuff to the right of the equals

          • payload.binding.continuation will give you the stuff after the binding, if we're in an expression.

          • payload.binding.value.args will give you the args? if the value is a function. not sure how you would manipulate the args

        • for a type

          • payload.type.name the name

          • payload.type.manifest the manifest

          • not sure what you can do with it.

[%macro.decorator.withList [%payload]; type arrayThing = array([%payload.type.name]) ]


  - Things to figure out
    - how to determine whether to replace the target (when it's a decorator) or not? maybe always replace. So will have to specify that it's a decorator.
      - macro.decorator

    - Do I want to be able to do any logic? maybe with like an `[%conditional if ([%arg "name"] == "awesome") { ... } else { ... } ]`. Yeah! call it `@eval` or `@preval`
      - would have a fairly limited set of comparisons you could do.

      - Exercise: can I reproduce conditional compilation? I'll want a way to rect to ENV vars
        - ```
[%macro.decorator.native
  if%eval ([%env "bsb-backend"] == "native") { [%payload] }
];
[%macro.decorator.js
  if%eval ([%env "bsb-backend"] == "js") { [%payload] }
];
 
[@native]
let platform = "native";
 
[@js]
let platform = "js";
  - What about a switch on multiple backends? like Platform.select
    - ```

[%macro.platform switch%eval ([%env "bsb-backend"]) { | "native" => [%arg "native"] | "js" => [%arg "js"] } ];

Examples of things I want to be able to do:

A platform macro

let%macro platform = (record: record) => switch%eval ([%env "bsb-backend"]) {
  | "native" => record.native
  | "js" => record.js
}

[%platform {
  | "native" => 10
  | "js" => 5
}]

----

let%macro.decorator js = (payload: expression) => if%eval ([%env "bsb-backend"] == "js") { payload } else {()};
let%macro.decorator.str js = (payload: structure_item) => if%eval ([%env "bsb-backend"] == "js") { payload } else {()};

let_ppx let%Anything style

let%macro.let async = (pattern, value, continuation) => {
  Js.Promise.then_(value, pattern => continuation)
}

let%async {something} = aPromise;
Js.Promise.resolve(ok)

->

Js.Promise.then_(aPromise, ({something}) => Js.Promise.resolve(ok))
let%macro.try async = (value, cases: rest(case)) => {
  Js.Promise.catch(value, err => [%construct.switch (err, cases)])
}

let caught = try%async (bPromise) {
  | "someText" => Js.Promise.resolve(ok)
  | exn => Js.Promise.reject(exn)
};

->

Js.Promise.catch(bPromise, err => switch err {
  | "someText" => Js.Promise.resolve(ok)
  | exn => Js.Promise.reject(exn)
})

(nope) A "super assert"

[%super_assert a == 5]

-> if (!(a == b)) {
  print_endline("a == 5")
  assert(false)
}

or maybe

[%assert_equal (a, b)]

->
if (a != b) {
  print_endline(ermmm ok I need type info here, darn)
}

Basic form

MacroTypes:

  • pattern (generic pattern, can do a switch on it)

  • expression (generic epxression, can switch probably)

  • ident - if this is an argument that is already a pattern, then we parse a pattern ident. Otherwise an expression ident.

  • (tuple literal) translates into the corresponding tuple form, for pattern or expr

  • record - some kind of record literal. attributes can be gotten out, and will be checked at macro evaluation time. Can also get the fields as array((ident, expr)), and the rest arg maybe?

  • js_object - a js object literal

  • string (string literal)

  • int (int literal)

  • float (float literal)

  • poly_variant - &backtick;Awesome

    • dunno about args. Might be nice to switch on the arg
    • but I guess I can cover that with - the type is an expression, and I do a switch on it.
    • yeah so maybe I don't need to be able to specify poly_variant? Unless I want to use the string of the variant name somehow. could be a future task

YOOO if I make this, I totally need an online playground for working with it. Must make jsoo compatible if at all possible.

Forms:

  • %macro - a basic macro, multiple arguments given as a tuple literal. non-expression arguments given via [%t: ] and [%p? ]
    • rest argument as the last one, others: rest(expression)
  • %macro.let has two forms:
    • let%macro.let a = (pattern: pattern, typ: type, expr: expression, expr: expression) =>
      • maybe cann this macro.let.typed?
    • let%macro.let a = (pattern: pattern, expr: expression, expr: expression) =>
  • %macro.let.toplevel
  • %macro.decorator
  • %macro.decorator.str
  • %macro.decorator.pat
  • %macro.decorator.typ
  • %macro.switch
    • let%macro.switch a = (expr: expression, (pattern, cexpr): case, cases: rest(case)) =>
    • not sure what can be done with this... other than constructing a new switch, I guess maybe with the pattern altered or something
    • [%construct.switch (expr, cases.map(((pattern, cexpr)) => ([%p? Some(pattern)], cexpr)))]
  • %macro.try
    • let%macro.try a = (expr: expression, cases: rest(case)) => Js.Promise.catch(expr, err => [%construct.switch (err, cases)])

Eval functions:

  • [%eval switch_(expr, cases)]

  • [%eval map(value, item => { /* treated the same as a macro body */ })] // if the body is a [%str], then will make multiple strs. Otherwise, will make a list literal probably?

  • [%eval foreach(value, item => {})] // if in a structure context, the body must be %str. Otherwise, will be exp_sequence'd together

  • let%eval something = otherthing // if it's a constant, will be processed as such. otherwise, will be a plain expr

  • if%eval (x == 2) {}

  • for%eval (x in 0 to 5) {} // if the body is a [%str], then it will make structured items. otherwise, will be exp_sequenced together.

  • [%construct.switch (expr, cases)] cases must be a list(case), expr must be some kind of expression

  • [%construct.let (pattern, expr, continuation)]

  • [%construct.let.typed (pattern, typ, expr, continuation)]

  • [%error "Some macro error message"]

    • would be nice to be able to provide an ast node to attach it to
  • [%construct.attribute (expr, string or ident)]

  • [%construct.js_attribute (expr, string or ident)]

  • [%env "some string"] get an environmental variable at compile-time, you can do logic with it, becomes a string literal

Transformations:

  • [%string! some_ast_node] - pretty prints it out using refmt
  • somearray.map(fn => thing) - transform an array of something into something else....
  • somearray.reduce(base, (collector, item) => collector) - reduce. not sure how far I can go with this
    • UPDATE: maybe get_in isn't even interesting
    • would be nice to make it so I can support get_in(some, ["a", "b", "c"])
    • even fancier get_in(some, ["a", &backquot;opt("b"), "c"])
let%macro get_in = (target: expression, path: list(string)) => {
  path.reduce(target, (target, item) => [%construct.attribute (target, item)])
}

// dunno if I can get this done tbh
let%macro get_in = (target: expression, path: list(expression)) => {
  path.reduce(target, (target, item) => switch%eval item {
    | `opt(expr) => switch ([%construct.]) {},
  });

  // yeah way too complicated
  loop((target, items), (target, items) => {
    switch%eval items {
      | [] => target
      | [one, ...rest] => [%recur (target, rest)]
    }
  })
}
let%macro name = (arg1: type1, arg2: type2) => {
  body
}

[%name (arg1, arg2)]

let%macro.let name = (pattern: pattern, expr: expression, continuation: expression) => {

}

let%macro name = (arg: array(string)) => {
  arg.map(item => [%eval.concat (item, "hello")])
}





->> instead of `arg`
I think I'd rather have it be
let%macro.record platform = record => {
  switch%eval ([%env "bsb-backend"]) {
    | "native" => record.native
    | "js" => record.js
  }
}
 
let x = [%platform {
  native: "someNative",
  js: "someJs"
}];
  - How to do arguments?
    - magically interpolate a record definition into arguments
      - so like
let x = [%platform {
  native: "someNative",
  js: "someJs",
}]
    - use `[@arg.somearg "contents"]` decorators
      - 
let x = [@arg.native "someNative"]
[@arg.js "someJs"]
[%platform]
[@t {
  name: "contents",
  otherThing: 10,
}]
//
[%macro.t
  Testing.run([%arg "name"], [%arg "otherThing"])
]

Some rust macros

https://doc.rust-lang.org/1.7.0/book/macros.html

#[macro_export]
macro_rules! vec {
    ( $( $x:expr ),* ) => {
        {
            let mut temp_vec = Vec::new();
            $(
                temp_vec.push($x);
            )*
            temp_vec
        }
    };
}


macro_rules! write_html {
    ($w:expr, ) => (());

    ($w:expr, $e:tt) => (write!($w, "{}", $e));

    ($w:expr, $tag:ident [ $($inner:tt)* ] $($rest:tt)*) => {{
        write!($w, "<{}>", stringify!($tag));
        write_html!($w, $($inner)*);
        write!($w, "</{}>", stringify!($tag));
        write_html!($w, $($rest)*);
    }};
}

fn main() {
    use std::fmt::Write;
    let mut out = String::new();

    write_html!(&mut out,
        html[
            head[title["Macros guide"]]
            body[h1["Macros are the best!"]]
        ]);

    assert_eq!(out,
        "<html><head><title>Macros guide</title></head>\
         <body><h1>Macros are the best!</h1></body></html>");
}

More Repositories

1

hexo-admin

An Admin Interface for Hexo
JavaScript
1,763
star
2

treed

Powerful Tree Editor
JavaScript
1,710
star
3

reason-language-server

A language server for reason, in reason
OCaml
659
star
4

gravitron

a little game
OCaml
490
star
5

rxvision

visualizer debugger for reactive streams
HTML
425
star
6

qmoji

🙃 Like mojibar, but written in swift
Swift
294
star
7

vim-debug

A plugin for VIM that creates an Integrated Debugging Environment :) Currently works with PHP and Python
Python
283
star
8

github-issues-viewer

A gitub issues viewer build in react + backbone
JavaScript
239
star
9

local-first

data syncing, storage, and collaboration. that works
JavaScript
220
star
10

milk

Milk 🥛 Stress-free serialization & deserialization for Reason/OCaml
Reason
203
star
11

veoluz

"I see light" - visualize the paths of millions of light rays through reflection, refraction and diffusion
Rust
153
star
12

redoc

A clean & easy documentation generator for reason/bucklescript/ocaml
JavaScript
117
star
13

stylecleanup

Find & fix unused styles in react native and aphrodite
JavaScript
114
star
14

fluid

OCaml
110
star
15

codetalker

A succinct, pythonic parser + translator solution
Python
100
star
16

let-anything

Deprecated, use the reasonml-community one
OCaml
99
star
17

ohai

easy setup from ocaml/reason native projects
OCaml
98
star
18

a-reason-react-tutorial

included code for A ReasonReact Tutorial
CSS
92
star
19

django-appsettings

A unified settings system for reusable django apps
Python
88
star
20

hooks-experimental

An experiment using react's new "hooks" with ReasonReact
OCaml
75
star
21

pack.re

a simple js bundler for reason
OCaml
72
star
22

reason-cli-tools

A cross-platform collection of useful utilities for making cli's in reason
OCaml
68
star
23

unison.rs

Scheme
65
star
24

reason_async

OCaml
65
star
25

reprocessing-scripts

OCaml
64
star
26

reepl

The cljs Read-eval-print-loop that really understands you
Clojure
63
star
27

reason-websocket

A websocket library for reason native
OCaml
57
star
28

django-feedback

A reusable django app to add an AJAX "feedback" tab to your site
Python
57
star
29

PJs

kinda like pyjamas, but quicker, cleaner, and easier. has the goal of generating readable, usable *robust* javascript code from python code
JavaScript
53
star
30

isomagic-todos

OCaml
51
star
31

terraform

rust + usgs data = 3d-printable models of mountains, canyons, etc.
Rust
51
star
32

myntax

OCaml
50
star
33

reprocessing-example-cross-platform

A boilerplate example for getting cross-platform reprocessing games off the ground
OCaml
50
star
34

purple-maze

A maze game written in reasonml
OCaml
45
star
35

rex-json

A simple cross-target JSON parser for Reason/OCaml
OCaml
45
star
36

reason-simple-server

A simple server library for native reason
OCaml
42
star
37

get_in_ppx

Reason
40
star
38

clevercss2

A complete rewrite of clevercss, utilizing the codetalker library
Python
39
star
39

pydbgp

A fork of Activestate's PyDBGp server
Python
37
star
40

reason-lisp-example

An example of using the lisp syntax for reason/ocaml
35
star
41

js_deep_ppx

[@js.deep] for immutably updating nested javascript objects in Reason/OCaml
OCaml
35
star
42

jerd

TypeScript
35
star
43

demobox

Demo Page Generator & Live Editor Component
JavaScript
35
star
44

minimist.re

A no-frills cli argument parser for reason
OCaml
34
star
45

f3d

OCaml
34
star
46

react-router

An integrated router for react
JavaScript
33
star
47

rusty-automata

Cellular Automata in Rust
Rust
29
star
48

vscode-background-terminal-notifier

Get a notification when a long-running terminal process completes in the background.
JavaScript
27
star
49

ppx_autoserialize

OCaml
27
star
50

hexo-renderer-handlebars

JavaScript
26
star
51

hexium

CSS
25
star
52

grow

Generative art
Rust
23
star
53

belt

Bucklescript's belt library packaged for native ocaml / dune / esy
OCaml
22
star
54

ocaml-cross-mobile

Shell
19
star
55

css

A small, fast css parser in python that utilizes the codetalker library
Python
16
star
56

drupal2django

A tool for migrating a blog from drupal to django; supports nodes, users, redirects, and more
Python
16
star
57

rsnpaint

experimenting with animations + reasonml
OCaml
15
star
58

mocha-selenium

Run selenium tests in parallel using mocha
JavaScript
15
star
59

lost-ranger

OCaml
15
star
60

reason_async_example

OCaml
15
star
61

hybrid-logical-clocks-example

JavaScript
14
star
62

tutorial-cljs

Clojure
14
star
63

reason-docker-server

An example cohttp server w/ dockerfile for deploying to now.sh
Dockerfile
14
star
64

esyi

OCaml
13
star
65

django-restive

A library for enabling the easy and intuitive creating of RESTful services in django
Python
13
star
66

react-teleporter

Make teleportable components
JavaScript
12
star
67

Contruct

the free open-source game creator by scirra
C++
12
star
68

babytux

A game based on babysmash, for occupying small children at the computer
Python
12
star
69

j3

Another attempt to realize my programming language
TypeScript
10
star
70

jaredly.github.io

node.js python data science golang philosophy faith. in no particular order
JavaScript
10
star
71

reason-bucklescript-example

Very bare bones starter pack for reason and bucklescript
OCaml
10
star
72

flame-rsn

OCaml
9
star
73

ssh-keypair

JavaScript
8
star
74

reason-docs

This is the old version: go to
JavaScript
7
star
75

letop-bs-example

OCaml
7
star
76

cowcow

http://cowcow.surge.sh/
OCaml
7
star
77

jfcom

repository for my website
Python
6
star
78

pbj

python build jelly - a simple, extensible pythonic build framework
Python
6
star
79

jnew

JavaScript
6
star
80

reactconf

Relay, Redux, Om/next Oh my!
JavaScript
6
star
81

type-safe-react-native

JavaScript
5
star
82

redu

A disk usage analyser, that's just a simple GUI on top of `du -sh`
OCaml
5
star
83

geometricart

TypeScript
5
star
84

advent-2017

OCaml
5
star
85

rocks

Go
5
star
86

itreed

Notablemind:repl a tree-based interface for iPython and Gorilla-repl
JavaScript
5
star
87

coqdocs

The docs I wish I had while learning Coq
Coq
5
star
88

blender

My blender scripts
Python
5
star
89

graphql-flow

Generate flowtypes for your graphql queries
JavaScript
4
star
90

ppx_guard

early returns for ocaml/reason
OCaml
4
star
91

j2

TypeScript
4
star
92

ppx_import

OCaml
4
star
93

type-safe-react

JavaScript
4
star
94

ferver

fe(a)rver - versioning for those of us who only care about breaking changes
4
star
95

pyjamas

a git clone of the sourceforge repo @ https://pyjamas.svn.sourceforge.net/svnroot/pyjamas
Python
4
star
96

move-over-electron

JavaScript
3
star
97

example-reason-codemod

OCaml
3
star
98

cowsay

A repo demonstrating the serializer generator "milk"
OCaml
3
star
99

code-review-checker

Sits in your mac menu par, checking for code reviews
Reason
3
star
100

bees

OCaml
3
star