Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/gfngfn/apbuf
Algebraic protocol buffers
https://github.com/gfngfn/apbuf
elm json-decoder json-encoder json-parser ocaml scala sesterl
Last synced: 2 months ago
JSON representation
Algebraic protocol buffers
- Host: GitHub
- URL: https://github.com/gfngfn/apbuf
- Owner: gfngfn
- Created: 2020-02-19T19:17:46.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2021-07-11T18:26:29.000Z (over 3 years ago)
- Last Synced: 2023-08-21T23:18:14.954Z (over 1 year ago)
- Topics: elm, json-decoder, json-encoder, json-parser, ocaml, scala, sesterl
- Language: OCaml
- Homepage:
- Size: 366 KB
- Stars: 13
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# APBuf (Algebraic Protocol Buffers)
## Summary
*APBuf* (*Algebraic Protocol Buffers*) is a compiler that generates decoders/encoders from a single configuration file that specifies the data format. It is characterized by the native support of “labeled direct sum” (which corresponds to ADTs (= algebraic data types) in Haskell, OCaml, Elm, etc.).
In exchange for its clean representation, APBuf at least currently does not pursue high computational performance.
* Currently supported target formats:
- JSON
* Currently supported target languages:
- Elm
- Scala
- [Sesterl](https://github.com/gfngfn/Sesterl)## How to install
Under the condition that `dune` and `make` are installed, invoke:
```console
$ make install
```and then the executable file `apbuf` will be installed.
## Example
The following configuration file:
```
@language_version "0.0.1"
@output "elm": {
dir = "./gen/elm/src",
module = "Bar",
}
@output "scala": {
dir = "./gen/play-scala-seed/app/assets/apbuf",
package = "apbufgen",
object = "Bar",
}geometry :=
| Rectangle : rectangle_info(int)
| Circle : circle_info(int, rational)rectangle_info($num) := {
upper_left : position($num),
lower_right : position($num),
}circle_info($cnum, $rnum) := {
center : position($cnum),
radius : $rnum,
}position($num) := { x : $num, y : $num }
rational := { numerator : int, denominator : int }
```produces Elm code that has the following API:
```elm
module Bar exposing (..)
import Json.Decode exposing (Decoder)
import Json.Encode exposing (Value)type Geometry
= Rectangle (RectangleInfo Int)
| Circle (CircleInfo Int Rational)
decodeGeometry : Decoder Geometry
encodeGeometry : Geometry -> Valuetype alias RectangleInfo a = { lowerRight : Position a, upperLeft : Position a }
decodeRectangleInfo : Decoder a -> Decoder (RectangleInfo a)
encodeRectangleInfo : (a -> Value) -> RectangleInfo a -> Valuetype alias CircleInfo c r = { center : Position c, radius : r }
decodeCircleInfo : Decoder c -> Decoder r -> Decoder (CircleInfo c r)
encodeCircleInfo : (c -> Value) -> (r -> Value) -> CircleInfo c r -> Valuetype alias Position a = { x : a, y : a }
decodePosition : Decoder a -> Decoder (Position a)
encodePosition : (a -> Value) -> { x : a, y : a } -> Valuetype alias Rational = { denominator : Int, numerator : Int }
decodeRational : Decoder Rational
encodeRational : Rational -> Value
```and also generates an implementation of the following API in Scala:
```scala
package apbufgenimport play.api.libs.json._
object Bar {
sealed trait Geometry
case class Rectangle(arg: RectangleInfo[Int]) extends Geometry
case class Circle(arg: CircleInfo[Int, Rational]) extends Geometry
def geometryReads(): Reads[Geometry]
def geometryWrites(): Writes[Geometry]case class RectangleInfo[Num](lowerRight: Position[Num], upperLeft: Position[Num])
def rectangleInfoReads[Num](Reads[Num]): Reads[RectangleInfo[Num]]
def rectangleInfoWrites[Num](Writes[Num]): Writes[RectangleInfo[Num]]case class CircleInfo[Cnum, Rnum](center: Position[Cnum], radius: Rnum)
def circleInfoReads[Cnum, Rnum](Reads[Cnum], Reads[Rnum]): Reads[CircleInfo[Cnum, Rnum]]
def circleInfoWrites[Cnum, Rnum](Writes[Cnum], Writes[Rnum]): Writes[CircleInfo[Cnum, Rnum]]case class Position[Num](x: Num, y: Num)
def positionReads[Num](Reads[Num]): Reads[Position[Num]]
def positionWrites[Num](Writes[Num]): Writes[Position[Num]]case class Rational(denominator: Int, numerator: Int)
def rationalReads(): Reads[Rational]
def rationalWrites(): Writes[Rational]
}
```## Syntax of configuration files
```
stringlit ::= (a string literal enclosed by double quotation marks)
ident ::= (a lowercased identifier)
$x ::= (a lowercased identifier with a preceding dollar sign)toplevel ::=
| metas declsmetas ::=
| meta metas
| (empty)meta ::=
| '@language_version' stringlit
| '@output' stringlit ':' dictdict ::=
| '{' fields '}'fields ::=
| ident '=' metaval ',' fields
| ident '=' metaval
| (empty)metaval ::=
| stringlit
| '[' metavals ']'metavals ::=
| metaval ',' metavals
| metaval
| (empty)// a list of declarations
decls ::=
| decl decls
| (empty)// a declaration
decl ::=
| binder ':=' msg
| binder ':=' '{' record '}'
| binder ':=' variant
| ident ':=' externalsbinder ::=
| ident
| ident '(' params ')'params ::=
| $x ',' params
| $x ','
| $xmsg ::=
| $x
| ident
| ident '(' args ')'args ::=
| msg ',' args
| msg ','
| msgrecord ::=
| ident ':' msg ',' record
| ident ':' msg
| (empty)variant ::=
| '|' ctor ':' msg variantsub
| ctor ':' msg variantsubvariantsub ::=
| '|' ctor ':' msg variantsub
| (empty)externals ::=
| external externals
| (empty)external ::=
| '@external' stringlit ':' dict
```## Features we want to support in the future
* Automated check for asserting backward compatibility of the update of data formats