├── .gitignore ├── .travis.yml ├── Er.g4 ├── Lexer.g4 ├── Makefile ├── README.md ├── TODO.md ├── criticisms.md ├── examples ├── README.md ├── download_manager.erl ├── download_manager.kju ├── etorrent_dht.erl ├── etorrent_dht.kju ├── glab_fd.erl ├── loop_swap.erl ├── loop_swap.kju ├── maps1.er ├── maps1.erl ├── math.er ├── math.erl ├── non_tail_var.er ├── non_tail_var.erl ├── parse.er ├── parse.erl ├── parse.sh ├── pool.erl ├── pool.kju ├── propfolding.erl ├── propfolding.kju └── snippets.er └── test ├── 1.png ├── 1.tree ├── 10.png ├── 10.tree ├── 11.png ├── 11.tree ├── 12.png ├── 12.tree ├── 13.png ├── 13.tree ├── 14.png ├── 14.tree ├── 15.png ├── 15.tree ├── 16.png ├── 16.tree ├── 17.png ├── 17.tree ├── 18.png ├── 18.tree ├── 19.png ├── 19.tree ├── 2.png ├── 2.tree ├── 20.png ├── 20.tree ├── 21.png ├── 21.tree ├── 22.png ├── 22.tree ├── 23.png ├── 23.tree ├── 24.png ├── 24.tree ├── 25.png ├── 25.tree ├── 26.png ├── 26.tree ├── 27.png ├── 27.tree ├── 28.png ├── 28.tree ├── 29.png ├── 29.tree ├── 3.png ├── 3.tree ├── 30.png ├── 30.tree ├── 31.png ├── 31.tree ├── 32.png ├── 32.tree ├── 33.png ├── 33.tree ├── 34.png ├── 34.tree ├── 35.png ├── 35.tree ├── 36.png ├── 36.tree ├── 37.png ├── 37.tree ├── 38.png ├── 38.tree ├── 39.png ├── 39.tree ├── 4.png ├── 4.tree ├── 40.png ├── 40.tree ├── 41.png ├── 41.tree ├── 42.png ├── 42.tree ├── 43.png ├── 43.tree ├── 44.png ├── 44.tree ├── 45.png ├── 45.tree ├── 46.png ├── 46.tree ├── 47.png ├── 47.tree ├── 48.png ├── 48.tree ├── 49.png ├── 49.tree ├── 5.png ├── 5.tree ├── 50.png ├── 50.tree ├── 51.png ├── 51.tree ├── 52.png ├── 52.tree ├── 53.png ├── 53.tree ├── 54.png ├── 54.tree ├── 55.png ├── 55.tree ├── 56.png ├── 56.tree ├── 57.png ├── 57.tree ├── 58.png ├── 58.tree ├── 59.png ├── 59.tree ├── 6.png ├── 6.tree ├── 60.png ├── 60.tree ├── 61.png ├── 61.tree ├── 62.png ├── 62.tree ├── 63.png ├── 63.tree ├── 64.png ├── 64.tree ├── 65.png ├── 65.tree ├── 66.png ├── 66.tree ├── 67.png ├── 67.tree ├── 68.png ├── 68.tree ├── 69.png ├── 69.tree ├── 7.png ├── 7.tree ├── 70.png ├── 70.tree ├── 71.png ├── 71.tree ├── 72.png ├── 72.tree ├── 8.png ├── 8.tree ├── 9.png ├── 9.tree └── check.sh /.gitignore: -------------------------------------------------------------------------------- 1 | Er*.class 2 | Er*.tokens 3 | Er*.java 4 | test/_* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | 3 | jdk: openjdk7 4 | 5 | sudo: false 6 | 7 | before_script: 8 | - set -e 9 | - cd $TRAVIS_BUILD_DIR 10 | - JAR=antlr-4.5.2-complete.jar 11 | - curl -O http://www.antlr.org/download/$JAR 12 | - export CLASSPATH=./$JAR:"$CLASSPATH" 13 | 14 | script: make test 15 | -------------------------------------------------------------------------------- /Er.g4: -------------------------------------------------------------------------------- 1 | // EBNF LL(*) grammar. Lexer split due to size. 2 | 3 | grammar Er; 4 | import Lexer; 5 | 6 | root : block* EOF ; 7 | 8 | block : defty 9 | | def 10 | | attribute ; 11 | 12 | /// Ops | Also some tokens as ANTLR4 concatenates lexemes. 13 | 14 | dim : '::' | '\u2237' ; // ∷ 15 | 16 | orelse : '||' | 'orelse' ; // || && are to replace their synonyms 17 | andalso : '&&' | 'andalso' ; 18 | 19 | compOp : '<' | '=<' | '==' | '>=' | '>' | '/=' | '=/=' | '=:=' 20 | | '\u2264'|'\u2265' | '\u2260'|'\u2248' | '\u2261'|'\u2262' ; // ≤ ≥ ≠ ≈ ≡ ≢ 21 | 22 | listOp : '++' | '--' ; 23 | 24 | addOp : '+' | '-' | 'bsl' | 'bsr' | 'or' | 'xor' | 'bor' | 'bxor' | '\u22c1' ; // ⋁ 25 | 26 | mulOp : '*' | '/' | 'div' | 'rem' | 'and' | 'band' | '\u22c0' ; // ⋀ 27 | 28 | unOp : '+' | '-' | 'not' | 'bnot' | '\u00ac' ; // ¬ 29 | 30 | etc : '...' | '\u2026' ; // … 31 | 32 | fun_ : 'fun' ; 33 | composeOp : '.' | '\u25e6' | '\u22c5' | '\u2022' ; // ◦ ⋅ • 34 | pipeOp : '|>' | '-<' | '\u2919' ; // ⤙ 35 | 36 | lra : '->' | '\u2192' ; // → 37 | bil : '<<' | '\u00ab' ; // « 38 | bir : '>>' | '\u00bb' ; // » 39 | 40 | generator : '<-' | '<=' | '<~' | '\u2190' | '\u21d0' | '\u219c' ; // ← ⇐ ↜ 41 | 42 | /// Tokens 43 | 44 | atom : Atom ; 45 | var : Var ; 46 | float_ : Float ; 47 | integer : Integer ; 48 | char_ : Char ; 49 | string : String | BString ; 50 | 51 | /// attribute 52 | 53 | attribute : atom (term|fas) 'of' (term|tyRecordFields) 54 | | atom (term|fas) ; 55 | 56 | fas: (fa (',' integer)*)+ ; 57 | fa : atom '/' integer ; 58 | 59 | tyRecordFields: '{' (tyRecordField (',' tyRecordField)*)? '}' ; 60 | tyRecordField : atom ('=' expr)? (dim type ('|' type)*)? ; 61 | 62 | /// def 63 | 64 | def : spec? func 65 | | spec? fun_func ; 66 | 67 | func : atom args guard? lra seqExprs ; 68 | 69 | fun_func : fa lra seqExprs ; 70 | 71 | args : '(' matchables? ')' ; 72 | 73 | // && || replaces Erlang's ,; (in guards) 74 | guard : 'when' expr ; 75 | 76 | /// defty 77 | 78 | defty : atom '(' tyMaxs? ')' dim tyMax ('when' tyGuards)? ; 79 | 80 | /// spec 81 | 82 | spec : atom dim tyFun ('when' tyGuards)? 83 | | fa dim (tyFun|subtype) ('when' tyGuards)? ; 84 | 85 | tyGuards: tyGuard+ ; 86 | tyGuard : subtype 87 | | (var dim)+ tyMax ; 88 | 89 | tyMaxs: tyMax (',' tyMax)* ; 90 | tyMax : (var dim)? type ('|' type)* ; 91 | 92 | subtype : atom (':' atom)* '(' tyMaxs? ')' ; 93 | 94 | type : type '..' type 95 | | type addOp type 96 | | type mulOp type 97 | | unOp type 98 | | '(' tyMax ')' 99 | | var | atom | integer 100 | | subtype 101 | | '[' ']' 102 | | '[' tyMax ']' 103 | | '[' tyMax ',' etc ']' 104 | | tyRecord 105 | | tyMap 106 | | '{' tyMaxs? '}' 107 | | tyBinary 108 | | fun_ '(' tyFun? ')' ; 109 | 110 | tyFun : '(' (etc|tyMaxs)? ')' lra tyMax ; 111 | 112 | tyRecord : '#{' atom '}' ; 113 | 114 | tyMap : '#{' tyMapAssocs? '}' ; 115 | tyMapAssocs: tyMapAssoc (',' tyMapAssoc)* ; 116 | tyMapAssoc : tyMax '=>' tyMax ; 117 | 118 | tyBinary : bil bir 119 | | bil tyBinaryBase bir 120 | | bil tyBinaryUnit bir 121 | | bil tyBinaryBase ',' tyBinaryUnit bir ; 122 | tyBinaryBase : var ':' type ; 123 | tyBinaryUnit : var ':' var '*' type ; 124 | 125 | /// expr | seqExprs | exprMax 126 | 127 | exprs: expr (',' expr)* ; 128 | expr : functionCall 129 | | expr '!' expr 130 | | expr mulOp expr 131 | | expr addOp expr 132 | | expr listOp expr 133 | | expr compOp expr 134 | | expr andalso expr 135 | | expr orelse expr 136 | | unOp expr 137 | | matchable '=' expr 138 | | mc | lc | bc | tc // Comprehensions 139 | | begin 140 | | if_ 141 | | cond 142 | | case_ 143 | | receive 144 | | fun 145 | | try_ 146 | | piped 147 | | expr composeOp expr 148 | | exprMax ; 149 | 150 | exprMax : var | '(' expr ')' 151 | | term | record ; 152 | 153 | seqExprs : expr+ ; 154 | 155 | matchables: matchable (',' matchable)* ; 156 | matchable : matchable mulOp matchable 157 | | matchable addOp matchable 158 | | matchable listOp matchable 159 | | unOp matchable 160 | | matchable '|' matchable // Disjoint union 161 | | matchable '=' matchable // Lesser precedence 162 | | var | '(' matchable ')' 163 | | term | record ; 164 | 165 | /// Detailed expressions 166 | 167 | params : '(' exprs? ')' ; 168 | functionCall : ':' params 169 | | atom params 170 | | (exprMax ':')+ exprMax params 171 | | '(' exprMax params ')' ; 172 | 173 | term : char_ 174 | | integer 175 | | float_ 176 | | string 177 | | atom 178 | | map 179 | | list 180 | | binary 181 | | tuple 182 | | lr | br | tr ; // Ranges 183 | 184 | list : '[' ']' 185 | | '[' expr tail ; 186 | tail : ']' 187 | | '|' expr ']' 188 | | ',' expr tail ; 189 | 190 | // Key-Value Stores 191 | record : recEmpty | recCreateMatch | recRead | '#{' expr atom recAssocs '}' ; 192 | map : mapEmpty | mapCreateMatch | mapRead | '#{' expr mapAssocs '}' ; 193 | recEmpty : '#{' atom '}' ; 194 | mapEmpty : '#{' '}' ; 195 | recCreateMatch : '#{' atom recAssocs '}' ; 196 | mapCreateMatch : '#{' mapAssocs '}' ; 197 | recRead : '#{' expr atom atom '}' ; 198 | mapRead : '#{' expr (functionCall|exprMax) '}' ; 199 | //^: (…) instead of expr disambiguates wrt recCreateMatch. 200 | recAssocs: recAssoc (',' recAssoc)* ; 201 | recAssoc : (atom|var) '=' expr ; 202 | mapAssocs: mapAssoc (',' mapAssoc)* ; 203 | mapAssoc : expr (':='|'=>') expr ; 204 | 205 | binary : bil binElements? bir ; 206 | binElements: binElement (',' binElement)* ; 207 | binElement : expr (':' exprMax)? ('/' binType ('-' binType)*)? ; 208 | binType : atom (':' integer)? ; 209 | 210 | tuple : '{' exprs? '}' ; 211 | 212 | lc : '[' seqExprs gens ']' ; 213 | bc : bil seqExprs gens bir ; 214 | mc : '#{' expr '=>' expr gens '}' ; //seqExprs? FIXME 215 | tc : '{' seqExprs gens '}' ; 216 | 217 | lr : '[' expr '..' expr ']' ; 218 | br : bil expr '..' expr bir ; 219 | tr : '{' expr '..' expr '}' ; 220 | 221 | begin : 'begin' seqExprs 'end' ; 222 | 223 | if_ : 'if' expr lra expr 'if' 'not' lra expr ; 224 | 225 | cond : 'cond' (condClause)+ 'end' ; 226 | condClause : expr lra seqExprs ; 227 | 228 | case_ : 'case' expr of 'end' ; 229 | 230 | receive : 'receive' clauses 'end' 231 | | 'receive' 'after' clause 'end' 232 | | 'receive' clauses 'after' clause 'end' ; 233 | 234 | fun : fun_ (':' | (exprMax ':')* exprMax) '/' (var|integer) 235 | | fun_ (':' | (exprMax ':')* exprMax) args '/' (var|integer) 236 | // | fun_ (':' | (atom ':')* atom ) args '/' (var|integer) 237 | | fun_ funClause+ 'end' ; 238 | 239 | try_ : 'try' seqExprs of? 'catch' catchClauses 'end' 240 | | 'try' seqExprs of? 'catch' catchClauses 'after' seqExprs 'end' 241 | | 'try' seqExprs of? 'after' seqExprs 'end' ; 242 | 243 | piped : exprMax (pipeOp fun)+ ; 244 | 245 | /// Utils | Exists mainly for compactness 246 | 247 | of : 'of' clauses ; 248 | 249 | clauses : (clause | clauseGuard)+ ; 250 | clause : matchable lra seqExprs ; 251 | clauseGuard : matchable guard lra seqExprs ; 252 | 253 | funClause : args guard? lra seqExprs ; 254 | 255 | catchClauses: catchClause+ ; 256 | catchClause : (exprMax ':')? (clause|clauseGuard) ; 257 | 258 | gen_ : '|' gen ; 259 | gens: gen_ (gen_ | gen | expr)* ; 260 | gen : matchable ':=' matchable '<-' expr 261 | | matchable generator expr ; 262 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GRA = Er 2 | 3 | all: $(GRA).tokens 4 | 5 | $(GRA).tokens: $(GRA).g4 Lexer.g4 6 | java org.antlr.v4.Tool $< 7 | javac $(GRA)*.java 8 | 9 | debug: $(GRA).tokens 10 | java org.antlr.v4.gui.TestRig $(GRA) root -encoding utf8 -gui 11 | 12 | clean: 13 | $(if $(wildcard $(GRA)*.tokens), \ 14 | rm -f $(GRA)*.class $(GRA)*.java $(GRA)*.tokens) 15 | 16 | distclean: clean 17 | $(if $(wildcard test/_*), rm -f test/_*) 18 | 19 | test: $(GRA).tokens 20 | ./test/check.sh examples/snippets.er 21 | .PHONY: test 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [er](https://github.com/fenollp/er) — Erlang's semantics with a simpler syntax 2 | 3 | [![Build Status](https://travis-ci.org/fenollp/er.svg?branch=master)](https://travis-ci.org/fenollp/er) 4 | 5 | 6 | The Er language: an attempt to improve Erlang's syntax quirks & horrible compile-time error reports. 7 | 8 | 9 | ```haskell 10 | zip :: ([A], [B]) -> [{A,B}] 11 | zip ([_|_]=Xs, [_|_]=Ys) -> 12 | [{X, Y} | X <- Xs 13 | | Y <- Ys] 14 | # Uses EEP19's comprehension multigenerators 15 | ``` 16 | 17 | 18 | ```c 19 | module some_attributes 20 | behaviour gen_server 21 | 22 | export start_link/0 23 | export init/1 24 | handle_call/3 25 | handle_cast/2 26 | handle_info/2 27 | terminate/2 28 | code_change/3 29 | record state of {} 30 | 31 | start_link () -> 32 | gen_server:start_link({local,?MODULE}, ?MODULE, [], []) 33 | 34 | init ([]) -> 35 | {ok, #{state}} 36 | handle_call (_Request, _From, State) -> 37 | {reply, ignored, State} 38 | handle_cast (_Msg, State) -> 39 | {noreply, State} 40 | handle_info (_Info, State) -> 41 | {noreply, State} 42 | terminate (_Reason, _State) -> ok 43 | code_change (_OldVsn, State, _Extra) -> 44 | {ok, State} 45 | ``` 46 | 47 | 48 | This project **implements a grammar** similar to Erlang's *while much less verbose*. Note: grammar is at worst `LL(*)` (to be determined). For example, Erlang could do without its “weird” line terminators while remaining ambiguity-free as a language (and you don't need to replace them with indentation-based parsing). Lua did it. Go kind of did it. Why wouldn't Erlang do it? also shut off the Erlang's-syntax-is-weird naysaying at the same time 49 | 50 | This project is also **a compiler** from said grammar to Core Erlang and has to meet the following constraints: 51 | * instant compile times 52 | * syntax error reports that live up to clang's 53 | * extending compile-passes should be easy 54 | * compiler should be built from grammar file 55 | 56 | 57 | ## Additions | Propositions | Ideas 58 | + Accepted 59 | + Keep lowercase as atoms, same for variables 60 | + Module name is filename (still only one per file) (only ASCII lowercase ∩ {non escape chars}) 61 | + Get rid of `,` `.` `;` as terminators|separators. Guards are be combined with `orelse` & `andalso` (as they often are) 62 | + Shorter type spec (ie `-type new_name :: old_name`: rm `-type`) 63 | + `-export([Fun/N|…]).` -> `export Fun/N …` 64 | + Rid lexer of legacy rust: `=<<`, `.`, `..`, `...` 65 | + `#[^\r\n{]*[\r\n]+` comments 66 | + Support `#warning`, `#ifdef` and the like 67 | + Use Haskell's way of setting per-file compile options 68 | + Some Unicode code points can match graphically similar operators, eg. `->` and `↦` 69 | + Add `..` operator. Meaning different inside `-spec` (does not produce a list there: `0..2 = 0|1|2` (EEP) 70 | + Add `FUNCTION` macro (same idea as `MODULE`) 71 | + `:` refers to the function being defined `:(I -1, [V|Acc])` (nice for recursive funs) 72 | + Change `||` to `|` in *-comprehensions (no ambiguity) 73 | + Keep `!` for sending messages. 74 | + No more Erlang's `if` nonsense. Only functional `if…then…else` and `case…of…end` and `cond`. 75 | + Support new (upcoming EEP) fun syntax: `fun io:format("~p\n")/1` = `fun (X) -> io:format("~p\n", X) end`. Forbid `fun lotsOfArgs(Arg1)/3(Arg4)/12` 76 | + Support for primes (`'` suffixes) in atom & variable names. Common case of `Var0/Var` and `f/f2`. 77 | + Support separating digits with `_` as in 1_000 and 100_000.42 78 | + {}-comprehensions and `[X..Y]` would put forth usage of tuples as immutable arrays as opposed to arrays and bins. Binaries can be thought of mutable when they're >64bits. Bad point: they need specificating type of cell when extracting (though bins of integers don't). Good point: tuples don't need that spec. Bad points: bins are more clever for now so should be faster, and there are nothing in R16B02 helping the use of tuples in that way. Good point: tuples are faster than arrays and they demonstrate the API tuples need. 79 | + Allow fundef: `‹atom Name› / ‹arity N› = …` for wrappers. Think about guards though. Can combine with EEP on fun. 80 | + API should put smaller data first for Currying purposes. `[modif(Str) | Str <- Strs] |> fun string:join(“, ”)/1` 81 | + STDlib funs get mapped without modulename when possible. Error on conflicts. eg: `fun '++'/2` sugars `fun erlang:'++'/2`. No import needed then. Lines shorter. Local defs > STDlib's. 82 | + Add `rev/1` (for lists) like `hd/1` or `tail/1`. Recursion and linked lists often make use of `lists:reverse/1`. 83 | + Allow position of generator right of `|` be unimportant. 84 | + `-compile(export_all).` not a language construct. Only the compile option. (Bad use is bad) 85 | + Make so that types can be defined in whatever order, like functions. 86 | + Have a real prepocessor (¬ https://gist.github.com/fenollp/8315947#file-ifdef_in_fun-erl) 87 | + `{·,1}` be `fun (_1) -> {_1,1} end` like in maths' `||·||` and such. 88 | + Erlang's `-type` does not accept `when` whereas `-spec` does. 89 | + Implement EEP19 by transforming to long-arity recursive fun. 90 | + Use `&&` and `||` for logic. Replace `andalso` and `orelse` 91 | + `-spec` type-variables defaults to `‹term›`, ‘to mimic Haskell's type parameters’ 92 | + compile-time suggestions on STDlib-found code: `sleep (I) = receive after I -> ok end` <> `timer:sleep(I)` 93 | + `export start/1,2`, since `start/1,2` is common notation for `start/1, start/2` in the literature 94 | + One of the jobs of `--static`: http://erlang.org/pipermail/erlang-questions/2010-June/051868.html 95 | + Get rid of the need of a package manager using Go's package semantics. There's *.app.src for that. 96 | + Unboxing & inlining such as `compile's inline_list_funcs`. And unboxing of stdlib functions. 97 | + Prepend `fun ` to º|•-notation. 98 | + `#include ` = `-include_lib("app1/include/app1.hrl").` If that fails then `-include("include/app1.hrl").` 99 | + `#include "include/defines.hrl" with ?macro1/0 ?macro2/2` for reading clarity 100 | + Allow `=` in guards as `=:=`, though warn about `==` & floats when possible 101 | + Rewrite unused-output *-comprehensions as `foreach/2`. Eg: `_ = [‹body› | ‹arg›<-‹input›]` becomes `lists:foreach(fun (‹arg›) -> ‹body› end, ‹input›)`. So as not to build an output. 102 | + `--static` should suggest to use `lists:flatmap/2` when it finds `lists:foldr(F,[])/1 ◊ lists:append/2`, … 103 | + `case Fun of fun M:F/N -> {M,F,N} _Lambda -> error end` should be allowed given matching exceptions is 104 | * Not rejected yet 105 | * define fun args invocation order (ltr?) so maps matching can be ellegant 106 | * new type: `pure(...)` which takes a `fun(...)` for docs & some warnings (Crashing can be a side effect...) 107 | * test system that uses func guards as a target when generating test data 108 | * JIT-like watchdog that collects metrics & attempts some optimization by hot swapping your modules 109 | * `try...after Timeout` that "ends your computation" if `Timeout`ms went by 110 | * `receive...after Node:Timeout` to timeout receiving from a pattern matchable `Node` (parts of *@*, as strings?) 111 | * `=` instead of `->` when defining a function (bad: dramatically slows parser down) 112 | * Explicit language support for behaviors and other OTP principles 113 | * A look-alike syntax for named & λ functions. More important: Haskell's λ functions. 114 | * `?` makes sense for records. Maybe use `?recordX{field_a}` syntax? 115 | * Define `?R16` and `?R16B01` -style macros for backward-compatible code. Or more like `#if ?ERTS_VSN = R16 …` 116 | * `--pedantic` requests Dialyzer type-specs on every exported function. Have all functions exported when debugging 117 | * Have Dialyzer warn on compile (such as to catch `-behavior` issues) 118 | * Simpler namespacing using Erlang's “library applications”? 119 | * Somehow bundle all Unicode sugaring as a whole 120 | * A boolean “matches” operator: `{where,_,_,_,_} =_ element(3,E)` 121 | * Support new (upcoming EEP) comprehensions: `[… | … <- …]`, `<<… | … <= …>>`, `{… | … <~ …}`. 122 | * `#{k => v}` and `#k_v{k => v}` are both maps but the second is statically checked. 123 | * `‹varT Id›{Key}` fetches key `Key` of record|map `Id` defined as a `varT`. 124 | * `ArgV[2]` | `ArgV[3..]` could be sugar for lists match-assign. Type for lists? Dialyzer? seems no… 125 | * `MsgA[2]` | `MsgA[3..]` could be sugar for tuple match-assign as well. For maps? no ordering, so no. 126 | * Note: `s/,//g` prevented by `B = A, (B)`. `B = A (B)` is a function call! An intermediary var would solve that ambiguity. 127 | * Note: `s/,//g` prevented by `B = A, #{b=B}`. `B = A #{b=B}` is a record creation! New syntax for records hinders this. 128 | * To counter EEP38-like problems, `--pedantic` would warn about funclauses apart from each other. 129 | * Add last `receive…end` clause: `_ -> '$Er_ignored_message'` (or warn on debug) 130 | * Provide a word to common spec `ok | {error, err_type()}` like `?oe(err_type())`. (Just an included macro?) 131 | * `<:` as the maps generator 132 | * Automatically-defined maps/records (inlined) getters (… not really Erlangish!) 133 | * A spec enables optimisation, eg: on calls to polymorphic functions. 134 | * Look into embedded specs to display terms. Eg: `f :: () -> ‹string› f()=[]`, `> f() #=> ""` 135 | * Grammar issues regarding record/maps and no `,`?: scan for 'Var#name'. 136 | * `a | b | c` as match-multiple notation. 137 | * Use typed record fields as in this illegal Erlang: `post_conditions(#output.schema_id) -> shall_tests()` 138 | - Rejected 139 | - Differenciate Erlang's multiple meanings of `=` with other shape-like tokens. 140 | - `'` instead of `$` for chars: `'a, 'b, '\', '\\, '\ `. It'd be nice to allow `$` at ^ of atoms. 141 | - Possibility following context to use keywords as atoms, like in C++. LALR? 142 | - Call behaviou?rs interfaces (same concept, helps user to feel she can make her own behaviors) 143 | - Er's `-spec` also adds the guards to a `spec`-ed function (or a `case…of…end` inside fun's clause). Don't add when guard explicitly written. 144 | - http://bugs.ruby-lang.org/issues/5054 145 | - See if `infix{,r,l} ‹0..9› ‹atom›` is possible with a visitor. `(larg0, larg1) atom (rargs) -> … ` could do! 146 | - Replace `-spec` types (like `any()`, `[integer()]`) with clearer: `‹any›`, `[‹integer›]` 147 | - Allow `:my_fun()` calls. Sugar for `?MODULE:my_fun()` calls if exported, [local func otherwise](http://erlang.org/pipermail/erlang-questions/2010-June/051772.html) (NO: introduces ambiguity) 148 | - Change `|` to `:` (but `M:F` hinders that) 149 | - In Er, *-comprehensions are computed in a non-explicit order. (most of the time are sugar for pmap) 150 | - `‹funNameAndArgs› ‹guard›* ‹| guarded-clause›* ‹end›` could describe a function. Like Haskell, yes. 151 | - Support https://en.wikipedia.org/wiki/SI_prefix#List_of_prefixes as: ‹number› ‹SI letter› 152 | - Allow using `<-` instead of the `receive` keyword. More dynamic impression of an agent receiving messages, Go's channels. 153 | - `#{k = v}` and `#k_v{k = v}` are records. The first is anonymous and maps to just `{v}` (not pretty). 154 | - Allow `* then ‹fun of arity ≥ 1›` in *-comprehensions such that: `[ x * 10 | x <- lst, x > 2, then sortWith by x ]`. 155 | - Allow hyphens/others to happen in atom names such as `tree-sum` (NO: there's already underscore) 156 | - Add Go-channels/ObjC-[]: `!!` sends then immediately receives using unique Ref. 157 | - Use `finally` in a `receive…end` in place of `after 0`. 158 | - Modules and files can have different names. As long as at the top of the file it references the right module. This allows for multiple files to be part of the same module. But how to compile modularly? 159 | - specs can be written with the Curry-arrow notation 160 | - Exporting `Mod:Fun/A` for wrapper modules. `Fun` locally defined => error. 161 | - Given `auto-spec-guards addition`, `--f-ascetic-guard-additions`. Don't add guards where Dialyzer says they are not needed. 162 | - `myfunc :: othermodule:similarfunc/2` ie. use `-spec`s from other modules (DRY). (Makes sense together with wrapper-notation) Enable only for STDlib's exported funcs? Cons: modularity is lost; odd that there is no `fun` keyword; cohabitation with `callback`s is unclear; more importantly, it leads to hiding stuff; maybe same spec, but linking it to another func's is weird as this func would behave differently. 163 | - Match integer ranges using “naked” `..` (2 cons: matching this is easy enough & naked `..` is worse thanusage of real ranges or disjoint union) 164 | 165 | ## Step 1 166 | * The whole point is to make Erlang/OTP look postmodern, readable and elegant. 167 | * Programs must be written for people to read, and only incidentally for machines to execute. — Abelson & Sussman, Structure and Interpretation of Computer Programs 168 | * The main point of a high-level programming language is powerful short programs. 169 | * The sole job of a programming language is to bridge the gap between ugly, but efficent, object code, and beautiful source code. -- slidetocode 170 | * An evolution, translation: somewhat backwards compatible since intersection is empty or harmless. 171 | * **Main point**: get rid of `, ; .` mess while **not doing indentation-based parsing**. 172 | * “The alternative to semicolons is making line endings significant.” Nope. 173 | * Unicode source code support. 174 | * This also fixes the things you may have taken for granted if you come from another landscape. 175 | * Support for [Joe's quotes](http://armstrongonsoftware.blogspot.fr/2012/06/what-happened-to-my-quotes.html): `‘’, “”`. `‘’`=`[]` (?) and `“”`=`<<>>`. 176 | * Compile to Core Erlang. clang level of expressivity in errors. Colors available. 177 | * **Lightning-fast compilation times** 178 | 179 | ## Step 2 180 | * Consistent stdlib. Whole rework of the API. Look at C++'s or Haskell's for containers. 181 | - Binaries are strings by default, not lists. 182 | * Additions to stdlib: UTF-8 strings, … 183 | - Compiling directly to BEAM? 184 | - Fully featured LLVM backend. 185 | * A package manager a-la Homebrew-Hackage-AUR. Community-managed and stuff. 186 | + A package is identified by its SCM address 187 | + A package version is a branch or a tag (invalidated when hash of tree changes, due to rebasing for example) 188 | * A certain and documented project architecture is enforced 189 | * Build metadata should not live in the project tree 190 | * Packages (& their versions) live in a flat namespace 191 | * Packages are compiled once, then symlinked for dev and copied for releases 192 | * To edit a dep change the symlink to an in-dev version of it 193 | * During build: dependencies are resolved, packages are fetched and colisions are checked 194 | * Xref, Dialyzer and a pretty printer should be executing fast and maybe in the background 195 | * Building updates code in the specified REPLs 196 | * A central folder holds compiled/fetched packages 197 | * Updates to the per-project package file (as well as updating deps) wipes generated files and starts anew 198 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To do 2 | 3 | ## Grammar 4 | * Devise grammar for `-callback`s 5 | * `callback` 6 | * Note: unused common ASCII operators: `%`, `^`, `&`, `~`, `backquote`, `;`, `\` 7 | * `|>`-notation for funs & comprehensions 8 | * Would it need `∘` | `º` ? 9 | * Restrict “holes” to inside pipes? They have to wrap around the uppermost `expr`… 10 | * Turn `try…of…catch…end` into `case…of…catch…end` to hinder `case catch`-ing code? 11 | * `-include`, `-include_lib` as `#include`* 12 | * Lex atoms: `^[0-9][^\s]+` 13 | * Add grammar for preprocessor, if possible. (`#` on col0, scrambled text between parens, …) 14 | * Move `and` & `or` precedences' has high as their exception-gulping counterparts (more power) 15 | * because `C == $\n or C == $\t` > `(C == $\n) or (C == $\t)` 16 | * Parse `(Mod:)?F/N` matching on modules' functions (as special a maching rule as exceptions) 17 | 18 | ## Parser 19 | * Haskell: [uu-parsinglib](http://hackage.haskell.org/package/uu-parsinglib) or `parsec` + lexer + pretty_printer 20 | * Or just look for a C backend to ANTLR4. Or an Erlang one. Or an haskell one. 21 | 22 | ## Documentation 23 | * Put up a pretty and quick one-pager™ on [fenollp.github.io/er](http://fenollp.github.io/er) 24 | * Clean up `README.md` and clarify direction. 25 | * Write a syntax colouring plugin for Pygments or emacs 26 | -------------------------------------------------------------------------------- /criticisms.md: -------------------------------------------------------------------------------- 1 | # Collected criticism against Erlang 2 | 3 | Research done on 2013.09.27 with the first Google results for “erlang criticism”. 4 | Some of the presented issues (ressentiments?) have been expressed sometimes 4 years 5 | prior to this work and may have been fixed. 6 | 7 | ### http://stackoverflow.com/questions/2199368/why-did-you-decide-against-using-erlang 8 | #### Dynamicaly typed 9 | …as opposed to Haskell's famous statically and infered typing system. 10 | #### String 11 | …or lack thereof. No native string type. 12 | #### DB drivers 13 | Tends to have poor (incomplete or unmaintained) database drivers 14 | #### Package manager 15 | It isn't batteries included and doesn't appear to have a community working on correcting this. If it does, it isn't highly visible. Haskell at least has Hackage, and I'd guess that's what has us choosing that language over any other. In Windows environments F# is about to have the ultimate advantage here. 16 | #### It's not C/Java 17 | * Because it looks alien from anyone used to the C family of languages 18 | * Because I wanted to be able to run on the Java Virtual Machine to take advantage of tools I knew and understood (like JConsole) and the years of effort which have gone into JIT and GC. 19 | * Because I didn't want to have to rewrite all the (Java) libraries I've built up over the years. 20 | * Because I have no idea about the Erlang "ecosystem" (database access, configuration, build etc). 21 | #### Awkward commas 22 | I didn't end up using erlang primarily because of syntax. 23 | 24 | ### http://lambda-the-ultimate.org/node/4350 25 | #### The problem with mutability 26 | The problem with mutability occurs when objects are shared. If mutability is adequately encapsulated (e.g., by not creating aliases to mutable objects), then its not that bad. In this case, encapsulating a mutable object within a process is definitely a good thing, though you might still run into trouble depending how the object is used within the process. 27 | #### Prolog legacy 28 | Well, all true, but having read the blog post in question, his complaint is really not with immutability per se, but rather with the ambiguity of binding and matching in Erlang (due to its roots in Prolog). The example he gives is something like: 29 | (Foo, Bar) <- blah... 30 | ... later ... 31 | (Baz, Bar) <- blah... 32 | The third line intends to shadow Bar, but in Erlang it will attempt a match of the previously bound Bar with the right-hand side. A mismatch will result in a run-time error, which is of course a terrible time to find out that you re-used a variable name. 33 | I agree with the blog post that this is probably the wrong default, and I can see why errors of this sort would be easy to make and very frustrating. But in my view it has essentially nothing to do with immutability. 34 | #### Syntax 35 | making the statement separators more uniform, and making the anonymous function syntax a bit more lightweight. (Though Erlang's anonymous functions isn't terribly worse than ML or Lisp: I suppose I've been spoiled by Haskell) 36 | 37 | ### http://www.unlimitednovelty.com/2011/07/trouble-with-erlang-or-erlang-is-ghetto.html 38 | #### no struct/map data structure 39 | #### Not really open 40 | While Erlang is an open source project, its implementation and release cycle are managed by Ericsson, the company that created it, and Ericsson just doesn't seem to care. I'm not sure what Ericsson's priorities are when it comes to adding features to Erlang, but in my opinion they're doing a worse job of engaging the community than Oracle has been doing with Java. 41 | #### No JIT 42 | Erlang has a "JIT" compiler called HiPE, which is mostly hype. I put JIT in quotes because HiPE is mostly an Erlang-to-native-code compiler with a limited set of backends which does a pretty bad job of optimizing and can't use runtime profiling information to improve the quality of the native code it generates in the way JIT compilers like HotSpot are able to. Calling HiPE a just-in-time compiler is a stretch as it is for most part an ahead-of-time native compiler for Erlang. The quality of native code produced by HiPE can be so poor that it's often outperformed by the userland bytecode interpreter implemented in BEAM. 43 | #### No clever mutability 44 | Immutable state languages force object creation whenever anything changes. This can be partially mitigated by persistent data structures, which are able to share bits and pieces of each other because they're immutable. This works, for example, when attempting to create a sublist that consists of the last N elements of a list. But what if you want the first N elements? You have to make a new list. What if you want elements M..N? You have to make a new list. 45 | In mutable state languages, performance problems can often be mitigated by mutating local (i.e. non-shared) state instead of creating new objects. To give an example from the Ruby language, combining two strings with the + operator, which creates a new string from two old ones, is significantly slower than combining two strings with the concatenating >> operator, which modifies the original string. Mutating state rather than creating new objects means there's fewer objects for the garbage collector to clean up and helps keep your program in-cache on inner loops. If you've seen Cliff Click's crash course on modern hardware, you're probably familiar with the idea that latency from cache misses is quickly becoming the dominating factor in today's software performance. Too much object creation blows the cache. 46 | #### Sometimes wrong/Bad error reporting 47 | {ok, Foo} = do_something(), 48 | ... 49 | {ok, Foo} = do_something_else(), 50 | ... 51 | view rawgistfile1.erl hosted with ❤ by GitHub 52 | The first pattern matching expression binds the Foo variable to something. In the second case, we've mistakenly forgot Foo was already bound. What's the result? 53 | exception error: no match of right hand side... 54 | We get no compiler warning in this case. This is the type of error you only encounter at runtime. It can lay undetected in your codebase, unless you're writing tests. 55 | #### The standard library is inconsistent, ugly, and riddled with legacy 56 | Should module names in the standard library be plural, like "lists"? Or should they be singular, like "string"? Should we count from 1, as in most of the functions found in things like the lists module, or should we count from 0 like the functions found in the array module? How do I get the length of a list? Is it lists:length/1? No, it's erlang:length/1. How do I get the Nth element of the tuple? Should I look in the tuple module? Wait, there is no tuple module! Instead it's erlang:element/2. How about the length of a tuple? It's erlang:tuple_size/1. Why is the length of a list just "length" whereas the length of a tuple is "tuple_size"? Wouldn't "list_length" be more consistent, as it calls out it works on lists? 57 | When we call erlang:now() to get the current time, it returns {1311,657039,366306}. What the hell does that mean? It's a tuple with three elements. How could time possible need three elements? A quick look at the documentation reveals that this tuple takes the form {Megaseconds, Seconds, Microseconds}. Separating out Microseconds makes sense... Erlang has no native decimal type so using a float would lose precision. But why split apart Megaseconds and Seconds? 58 | Once upon a time Erlang didn't support integers large enough to store the combination of Megaseconds and Seconds, so they were split apart. The result is a meaningless jumble of three numbers, which you have to run through the confusingly named calendar:now_to_local_time/1 function to get a human meaningful result, which doesn't tell you what time it is now, but instead takes the tuple that erlang:now/0 returns as an argument and will spit back meaningful {Year, Month, Day} and {Hour, Minute, Second} tuples 59 | #### Syntax/Legacy 60 | Try to use "query" as an atom in Erlang, e.g. {query, "SELECT * FROM foobar"}. What happens? 61 | syntax error before: ',' 62 | This is because 'query' is a reserved word which was reserved for Mnemosyne queries. Never heard of Mnemosyne? That's because it's an archaic way of querying Erlang's built-in database, Mnesia, and has been replaced with Query List Comprehensions (QLC). However, it remains around for backwards compatibility. 63 | You can't use "query" as a function name. You can't tag a tuple with "query". You can't do anything with "query" except invoke a deprecated legacy API which no one uses anymore. 64 | Even attempting to use "let" in Erlang just yields: syntax error before: 'let' 65 | Once upon a time Erlang was supposed to get let bindings, and the "let" keyword was set aside for this purpose. But much like frames, it never happened. Instead, let is now an unimplemented reserved word which just breaks your programs. 66 | In Clojure, I can write the following: (if false :youll-never-know). This implicitly returns "nil" because the condition was false. What's the equivalent Erlang? 67 | if 68 | false -> youll_never_know; 69 | true -> void 70 | end. 71 | view rawgistfile1.erl hosted with ❤ by GitHub 72 | Erlang forces you to specify a clause that always matches regardless of whether you care about the result or not. If no clause matches, you get the amazingly fun "badmatch" exception. In cases where you don't care about the result, you're still forced to add a nonsense clause which returns a void value just to prevent the runtime from raising an exception. 73 | #### No strings 74 | Erlang has no way of differentiating lists of integers that represent strings from lists of integers that are actually lists of integers. 75 | much of the tooling and string functions are designed to work with list-based strings. To leverage these functions, you have to convert a binary to a list before working with it. 76 | Arguing about strings with the old Erlangers is akin to stabbing oneself in the eye with a butter knife. I have re-implented a string object based on binaries, complete with encoding-aware string functions. The response on the list was basically "Why would we need that? A list of integers is fine!" Luddites, really... 77 | 78 | ### http://rebelscience.blogspot.co.uk/2007/08/seven-deadly-sins-of-erlang.html 79 | #### Does not prevent race conditions 80 | It has no mechanism for automatically enforcing deterministic timing at the operation level. In other words, operations in an Erlang program are neither reactive nor synchronous. This is an absolute must for rock-solid reliability. Note: Synchronous processing is not the same as synchronous messaging. 81 | #### It is implicitly sequential 82 | You would think that your looping process can receive and treat messages in parallel, given the fuss around state. Well think again. 83 | It is explicitly concurrent. Bad for the same reason. 84 | #### Commas & Package manager 85 | Last but not least, Erlang does not have inherent support for plug-compatibility (drag and drop programming) and a searchable/browsable object repository. This is essential to effective and fast software composition and reusability. 86 | 87 | ### http://damienkatz.net/2008/03/what_sucks_abou.html 88 | #### Syntax 89 | caommas, ifs 90 | #### Warts 91 | strings, records, single assigment even though SSA 92 | #### Code Organization 93 | The only code organization offered is the source file module, there are no classes or namespaces. I'm no OO fanatic (not anymore), but I do see the real value it has: code organization. 94 | Every time time you need to create something resembling a class (like an OTP generic process), you have to create whole Erlang file module, which means a whole new source file with a copyright banner plus the Erlang cruft at the top of each source file, and then it must be added to build system and source control. The extra file creation artificially spreads out the code over the file system, making things harder to follow. 95 | What I wish for is a simple class facility. I don't need inheritance or virtual methods or static checking or monkey patching. I'd just like some encapsulation, the ability to say here is a hunk of data and you can use these methods to taste the tootsie center. That would satisfy about 90% of my unmet project organization needs. 96 | #### Uneven Libraries and Documentation 97 | Most of core Erlang is well documented and designed, but too many of the included modules are buggy, overly complex, poorly documented or all three. 98 | The Inets httpd server we've found incredibly frustrating to use in CouchDB and are discarding it for a 3rd party Erlang HTTP library. The XML processor (Xmerl) is slow, complicated and under documented. Anything in Erlang using a GUI, like the debugger or process monitor, is hideous on Windows and pretty much unusable on OS X. The OTP build and versioning system is complicated and verbose and I still don't understand why it is like it is. 99 | And crufty. I know Erlang has been evolving for real world use for a long time, but that doesn't make the cruftyness it's accumulated over the years smell any better. The coding standards in the core Erlang libraries can differ widely, with different naming, argument ordering and return value conventions. It's tolerable, but it's still there and you must still deal with it. 100 | 101 | ### http://erlang.org/pipermail/erlang-questions/2001-February/002669.html 102 | #### Syntax 103 | commas 104 | #### No user guards 105 | #### Poor exceptions report 106 | Another vast and poorly lit area is that of exceptions. Today, 107 | there is seldom any way of knowing what sorts of exceptions a function 108 | can throw, and the exceptions are usually uninformative (e.g., 109 | "badmatch" when you have half a dozen pattern matches in the indicated 110 | function). Thus, _using_ the exit reason to recover from an error is 111 | difficult. In practice, you print, log or ignore an exit today. 112 | 113 | ### https://medium.com/@elbrujohalcon/there-are-guards-and-guards-71e67d4975d7#.mb942l9z2 114 | #### Syntax 115 | surprising operator precedence 116 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | % Don't forget those: 2 | % http://www.erlang.org/doc/programming_examples/users_guide.html 3 | 4 | # Programming Examples User Guide 5 | 6 | ## Records 7 | 8 | --- 9 | 10 | ## Funs 11 | 12 | ### Example 1 - `map` 13 | If we want to double every element in a list, we could write a function named double: 14 | ```haskell 15 | double([H|T]) = [2*H|double(T)] 16 | double([]) = [] 17 | ``` 18 | 23 | 24 | We now add the function add_one, which adds one to every element in a list: 25 | ```haskell 26 | add_one([H|T]) = [H+1|add_one(T)] 27 | add_one([]) = [] 28 | ``` 29 | 30 | These functions, `double` and `add_one`, have a very similar structure. 31 | We can exploit this fact and write a function `map` which expresses this similarity: 32 | ```haskell 33 | map(F, [H|T]) = [F(H)|map(F, T)] 34 | map(F, []) = [] 35 | ``` 36 | 37 | We can now express the functions `double` and `add_one` in terms of `map` as follows: 38 | ```haskell 39 | double(L) = map(fun(X) -> 2*X end, L) 40 | add_one(L) = map(fun(X) -> 1 + X end, L) 41 | ``` 42 | 43 | `map(F, List)` is a function which takes a function `F` and a list `L` as arguments 44 | and returns the new list which is obtained by applying `F` to each of the elements in `L`. 45 | 46 | The process of abstracting out the common features of a number of different programs 47 | is called procedural abstraction. Procedural abstraction can be used in order 48 | to write several different functions which have a similar structure, 49 | but differ only in some minor detail. This is done as follows: 50 | 51 | * write one function which represents the common features of these functions 52 | * parameterize the difference in terms of functions which are passed as arguments to the common function. 53 | 54 | ### Example 2 -`foreach` 55 | This example illustrates procedural abstraction. Initially, we show the following two examples written as conventional functions: 56 | 57 | * all elements of a list are printed onto a stream 58 | * a message is broadcast to a list of processes. 59 | 60 | ```haskell 61 | print_list(Stream, [H|T]) = 62 | io:format(Stream, "~p~n", [H]) 63 | print_list(Stream, T) 64 | print_list(Stream, []) = 65 | true 66 | ``` 67 | 68 | ```haskell 69 | broadcast(Msg, [Pid|Pids]) = 70 | Pid ! Msg 71 | broadcast(Msg, Pids) 72 | broadcast(_, []) = 73 | true 74 | ``` 75 | 76 | Both these functions have a very similar structure. 77 | They both iterate over a list doing something to each element in the list. 78 | The "something" has to be carried round as an extra argument to the function which does this. 79 | 80 | The function `foreach` expresses this similarity: 81 | ```haskell 82 | foreach(F, [H|T]) = 83 | F(H) 84 | foreach(F, T) 85 | foreach(F, []) = 86 | ok 87 | ``` 88 | 89 | Using `foreach`, `print_list` becomes: 90 | ```haskell 91 | foreach(fun(H) -> io:format(S, "~p~n",[H]) end, L) 92 | ``` 93 | 94 | `broadcast` becomes: 95 | ```haskell 96 | foreach(fun(Pid) -> Pid ! M end, L) 97 | ``` 98 | 99 | `foreach` is evaluated for its side-effect and not its value. 100 | `foreach(Fun, L)` calls `Fun(X)` for each element `X` in `L` and the processing 101 | occurs in the order in which the elements were defined in `L`. 102 | `map` does not define the order in which its elements are processed. 103 | 104 | ### The Syntax of Funs 105 | Funs are written with the syntax: 106 | ```haskell 107 | F = fun (Arg1, Arg2, … ArgN) -> 108 | … 109 | end 110 | ``` 111 | This creates an anonymous function of `N` arguments and binds it to the variable `F`. 112 | 113 | If we have already written a function in the same module and wish to pass this function 114 | as an argument, we can use the following syntax: 115 | ```haskell 116 | F = fun FunctionName/Arity 117 | ``` 118 | With this form of function reference, the function which is referred to does not need to be exported from the module. 119 | 120 | We can evaluate the fun F with the syntax: 121 | ```haskell 122 | F(Arg1, Arg2, ..., ArgN) 123 | ``` 124 | 125 | The following program illustrates the different ways of creating funs: 126 | ```haskell 127 | ## module fun_test 128 | export t1/0 t2/0 double/1 end 129 | import map/2 from lists 130 | 131 | t1() = map(fun(X) -> 2 * X end, [1,2,3,4,5]) 132 | 133 | t2() = map(fun double/1, [1,2,3,4,5]) 134 | 135 | double(X) = X * 2 136 | ``` 137 | 138 | To check whether a term is a fun, use the test `is_function/1` in a guard. Example: 139 | ```haskell 140 | f(F, Args) when is_function(F) = 141 | apply(F, Args) 142 | f(N, _) when is_integer(N) = 143 | N 144 | ``` 145 | 146 | Funs are a distinct type. The BIFs `erlang:fun_info/1,2` can be used to retrieve 147 | information about a fun, and the BIF `erlang:fun_to_list/1` returns a textual 148 | representation of a fun. The `check_process_code/2` BIF returns `true` if the process 149 | contains funs that depend on the old version of a module. 150 | 151 | ### Variable Bindings Within a Fun 152 | The scope rules for variables which occur in funs are as follows: 153 | 154 | * All variables which occur in the head of a fun are assumed to be "fresh" variables. 155 | * Variables which are defined before the fun, and which occur in function calls or 156 | guard tests within the fun, have the values they had outside the fun. 157 | * No variables may be exported from a fun. 158 | 159 | The following examples illustrate these rules: 160 | ```haskell 161 | print_list(File, List) = 162 | {ok, Stream} = file:open(File, write) 163 | foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List) 164 | file:close(Stream) 165 | ``` 166 | 167 | In the above example, the variable `X` which is defined in the head of the fun 168 | is a new variable. The value of the variable `Stream` which is used within the fun 169 | gets its value from the `file:open` line. 170 | 171 | Since any variable which occurs in the head of a fun is considered a new variable 172 | it would be equally valid to write: 173 | ```haskell 174 | print_list(File, List) = 175 | {ok, Stream} = file:open(File, write) 176 | foreach(fun(File) -> 177 | io:format(Stream,"~p~n",[File]) 178 | end, List) 179 | file:close(Stream) 180 | ``` 181 | 182 | 183 | 184 | ### Funs and the `lists` Module 185 | The following examples show a dialogue with the Erlang shell. 186 | All the higher order functions discussed are exported from the module `lists`. 187 | 188 | ### `map` 189 | ```haskell 190 | map(F, [H|T]) = [F(H)|map(F, T)] 191 | map(F, []) = [] 192 | ``` 193 | 194 | 195 | ### `any` 196 | ```haskell 197 | any(Pred, [H|T]) = 198 | if Pred(H) then true 199 | else any(Pred, T) end 200 | any(Pred, []) 201 | false 202 | ``` 203 | 204 | 205 | ### `all` 206 | ```haskell 207 | all(Pred, [H|T]) = 208 | if Pred(H) then all(Pred, T) 209 | else false 210 | end 211 | all(Pred, []) = 212 | true 213 | ``` 214 | 215 | 216 | ### `foreach` 217 | ```haskell 218 | foreach(F, [H|T]) = 219 | F(H) 220 | foreach(F, T) 221 | foreach(F, []) = 222 | ok 223 | ``` 224 | 225 | 226 | ### `foldl` 227 | ```haskell 228 | foldl(F, Accu, [Hd|Tail]) = 229 | foldl(F, F(Hd, Accu), Tail) 230 | foldl(F, Accu, []) = Accu 231 | ``` 232 | 233 | 234 | ### `mapfoldl` 235 | ```haskell 236 | mapfoldl(F, Accu0, [Hd|Tail]) = 237 | {R,Accu1} = F(Hd, Accu0) 238 | {Rs,Accu2} = mapfoldl(F, Accu1, Tail) 239 | {[R|Rs], Accu2} 240 | mapfoldl(F, Accu, []) = {[], Accu} 241 | ``` 242 | 243 | 244 | ### `filter` 245 | ```haskell 246 | filter(F, [H|T]) = 247 | if F(H) then [H|filter(F, T)] 248 | else filter(F, T) 249 | end 250 | filter(F, []) = [] 251 | ``` 252 | 253 | 254 | ### `takewhile` 255 | ```haskell 256 | takewhile(Pred, [H|T]) = 257 | if Pred(H) 258 | then [H|takewhile(Pred, T)] 259 | else [] 260 | end 261 | takewhile(Pred, []) = 262 | [] 263 | ``` 264 | 265 | 266 | ### `dropwhile` 267 | ```haskell 268 | dropwhile(Pred, [H|T]) = 269 | if Pred(H) then 270 | dropwhile(Pred, T) 271 | else [H|T] 272 | end 273 | dropwhile(Pred, []) = 274 | [] 275 | ``` 276 | 277 | 278 | ### `splitwith` 279 | ```haskell 280 | splitwith(Pred, L) = 281 | splitwith(Pred, L, []) 282 | 283 | splitwith(Pred, [H|T], L) = 284 | if Pred(H) then 285 | splitwith(Pred, T, [H|L]) 286 | else 287 | {reverse(L), [H|T]} 288 | end 289 | splitwith(Pred, [], L) = 290 | {reverse(L), []} 291 | ``` 292 | 293 | 294 | 295 | 296 | --- 297 | 298 | ## List Comprehensions 299 | 300 | ### Quick Sort 301 | The well known quick sort routine can be written as follows: 302 | ```haskell 303 | sort([Pivot|T]) = 304 | sort([ X | X <- T X < Pivot]) ++ 305 | [Pivot] ++ 306 | sort([ X | X <- T X ≥ Pivot]) 307 | sort([]) = [] 308 | ``` 309 | The expression `[X | X <- T X < Pivot]` is the list of all elements in `T`, 310 | which are less than `Pivot`. 311 | 312 | To sort a list, we isolate the first element in the list and split the list into two sub-lists. 313 | The first sub-list contains all elements which are smaller than the first element in the list, 314 | the second contains all elements which are greater than or equal to the first element in the list. 315 | We then sort the sub-lists and combine the results. 316 | 317 | ### Permutations 318 | The following example generates all permutations of the elements in a list: 319 | ```haskell 320 | perms([]) = [[]] 321 | perms(L) = [[H|T] | H <- L T <- perms(L--[H])] 322 | ``` 323 | We take take `H` from `L` in all possible ways. 324 | The result is the set of all lists `[H|T]`, where `T` is the set of all possible 325 | permutations of `L` with `H` removed. 326 | 327 | 328 | ### Simplifications with List Comprehensions 329 | As an example, list comprehensions can be used to simplify some of the functions in `lists.erl`: 330 | ```haskell 331 | append(L) = [ X | L1 <- L X <- L1] 332 | map(Fun, L) = [Fun(X) | X <- L ] 333 | filter(Pred, L) = [ X | X <- L Pred(X)] 334 | ``` 335 | 336 | ### Variable Bindings in List Comprehensions 337 | --- 338 | 339 | ## Bit Syntax 340 | 341 | --- 342 | -------------------------------------------------------------------------------- /examples/download_manager.erl: -------------------------------------------------------------------------------- 1 | %%%--------------------------------------------------------------------- 2 | %%% Created by: Fredrik Gustafsson 3 | %%% Creation date: 2011-10-18 4 | %%% Refactored date: 2011-11-23 5 | %%%--------------------------------------------------------------------- 6 | %%% Description module downloading_manager 7 | %%%--------------------------------------------------------------------- 8 | %%% Parses the torrent file and handles the content 9 | %%% which will be handed over to its children 10 | %%%--------------------------------------------------------------------- 11 | %%% Exports 12 | %%%--------------------------------------------------------------------- 13 | %%% start(.*torrent file name, Pid of gui module) 14 | %%% spawns a new process running the init method and returns its pid. 15 | %%%--------------------------------------------------------------------- 16 | %%% init(.*torrent file name, Pid of gui module) 17 | %%% initiates the information in the torrent file and 18 | %%% spawns peers 19 | %%%--------------------------------------------------------------------- 20 | %%% is_valid_info_hash() 21 | %%% checks if the info hash from peer is the same as 22 | %%% ours. 23 | %%%--------------------------------------------------------------------- 24 | %%% get_my_id() 25 | %%% Returns our id. 26 | %%%--------------------------------------------------------------------- 27 | %%% get_my_info_hash() 28 | %%% Returns the info hash for the current task 29 | %%%--------------------------------------------------------------------- 30 | %%% get_info_clean() 31 | %%% Returns the info hash in another clean format 32 | %%%--------------------------------------------------------------------- 33 | 34 | -module(download_manager). 35 | -export([start/2, init/2, is_valid_info_hash/2, 36 | get_my_id/1, get_my_info_hash/1, get_info_clean/1]). 37 | 38 | %% Functions exported for testing only commented further down 39 | -export([get_torrent_data/1, get_announce_list/1, get_pieces/1, 40 | get_piece_length/1,get_length_and_name/1]). 41 | 42 | start(File, GUIPid) -> 43 | spawn_link(?MODULE, init, [File, GUIPid]). 44 | 45 | init(File, GUIPid) -> 46 | process_flag(trap_exit, true), 47 | link(GUIPid), 48 | {dict, Dict} = get_torrent_data(File), 49 | Info_raw = dict:fetch(<<"info">>, Dict), 50 | Info_bencoded = bencode:encode(Info_raw), 51 | Nr_of_pieces = length(get_pieces({dict, Dict})), 52 | Info_hash = list_to_binary(sha:sha1raw(Info_bencoded)), 53 | GUIPid ! {hash, {sha:sha1hash(Info_hash),Nr_of_pieces}}, 54 | Peers_pid = peers:start(self(), get_announce_list({dict, Dict}), 55 | get_pieces({dict, Dict}), 56 | get_piece_length({dict, Dict}), 57 | get_length_and_name({dict, Dict}), File), 58 | link(Peers_pid), 59 | loop(Peers_pid, Info_hash,Info_bencoded, gui_basic:createUniqueId(), 60 | GUIPid, 0). 61 | 62 | is_valid_info_hash(Info_from_peer, Pid) -> 63 | Pid ! {valid_info, self(), Info_from_peer}, 64 | receive 65 | Any -> Any end. 66 | 67 | get_my_id(Dl_pid) -> 68 | Dl_pid ! {get_id, self()}, 69 | receive 70 | {reply, Reply} -> 71 | Reply 72 | end. 73 | 74 | get_my_info_hash(Dl_pid) -> 75 | Dl_pid ! {get_my_info_hash, self()}, 76 | receive 77 | {reply, Reply} -> 78 | Reply 79 | end. 80 | 81 | get_info_clean(Dl_pid) -> 82 | Dl_pid ! {get_clean_info, self()}, 83 | receive 84 | {reply, Reply} -> 85 | Reply 86 | end. 87 | 88 | %%-------------------------------------------------------------------- 89 | %% Function: loop/1 90 | %% Purpose: Sends information to GUI and corresponds 91 | %% to messages from its children. 92 | %% Args: Peers_pid: The pid of peers module 93 | %% Info_hash: Our info hash format 1 94 | %% Info_clean: Our info hash format 2 95 | %% My_id: Our unique id. 96 | %% GUI_pid : The pid of gui module 97 | %% Counter that checks so that we send a message 98 | %% only once to GUI. 99 | %% Returns:- 100 | %%-------------------------------------------------------------------- 101 | 102 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) -> 103 | receive 104 | {get_clean_info, From} -> 105 | From ! {reply, Info_clean}, 106 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter); 107 | {valid_info, From, Info_from_peer} -> 108 | From ! binary_to_list(Info_hash) =:= Info_from_peer, 109 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter); 110 | {get_id, From} -> 111 | From ! {reply, My_id}, 112 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter); 113 | {get_my_info_hash, From} -> 114 | From ! {reply, Info_hash}, 115 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter); 116 | {this_tracker, Tracker} -> 117 | GUI_pid ! {tracker, Tracker}, 118 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter); 119 | {'EXIT', Peers_pid, _Reason} -> 120 | exit(self(), kill); 121 | stop -> ok 122 | after 3000 -> 123 | Peers_pid ! {get_downloaded, self()}, 124 | receive 125 | {reply, {Downloaded, Current_pieces}} -> 126 | case Downloaded of 127 | 100 -> 128 | case Counter of 129 | 0 -> 130 | GUI_pid ! {percentage, {Downloaded, 131 | Current_pieces}}, 132 | loop(Peers_pid, Info_hash, Info_clean, 133 | My_id, GUI_pid, 1); 134 | _ -> 135 | ok 136 | end; 137 | _ -> 138 | GUI_pid ! {percentage, {Downloaded, Current_pieces}} 139 | end 140 | end, 141 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 142 | end. 143 | 144 | %%-------------------------------------------------------------------- 145 | %% Function: get_torrent_data/1 146 | %% Purpose: decodes the torrent file 147 | %% Args: File: The file path to the torrent file 148 | %% Returns: Either, {dict, Dict} when the torrent file 149 | %% has been successfully read or {error, no_file} if 150 | %% the file cant be read. 151 | %%-------------------------------------------------------------------- 152 | 153 | get_torrent_data(File) -> 154 | case file:read_file(File) of 155 | {ok, Text} -> 156 | case bencode:decode(Text) of 157 | {{dict, Dict}, _Remainder} -> 158 | {dict, Dict} 159 | end; 160 | _ -> 161 | {error, no_file} 162 | end. 163 | 164 | %%-------------------------------------------------------------------- 165 | %% Function: get_announce_list/1 166 | %% Purpose: Returns the list of trackers in the 167 | %% announce list. 168 | %% Args: {dict, Dict}: The dictionary of the torrent 169 | %% file 170 | %% Returns: The list of trackers. 171 | %%-------------------------------------------------------------------- 172 | 173 | get_announce_list({dict, Dict}) -> 174 | case dict:find(<<"announce-list">>, Dict) of 175 | {ok,{_, List}} -> 176 | List; 177 | error -> 178 | {ok, Link} = dict:find(<<"announce">>, Dict), 179 | [{ok, binary_to_list(Link)}] 180 | end. 181 | 182 | %%-------------------------------------------------------------------- 183 | %% Function: get_pieces/1 184 | %% Purpose: Grabs the pieces from the dictionary 185 | %% Args: {dict, Dict}, the dictionary of the torrent file 186 | %% Returns: The list of the pieces 187 | %%-------------------------------------------------------------------- 188 | 189 | get_pieces({dict, Dict}) -> 190 | {_, Info_dict} = dict:fetch(<<"info">>, Dict), 191 | Pieces = dict:fetch(<<"pieces">>, Info_dict), 192 | handle_pieces(binary_to_list(Pieces),[], 1, []). 193 | 194 | %%-------------------------------------------------------------------- 195 | %% Function: get_piece_length/1 196 | %% Purpose: Grabs the piece length of the task from 197 | %% the dictionary of the torrent file. 198 | %% Args: {dict, Dict}, dictionary of the torrent file 199 | %% Returns: The piece length 200 | %%-------------------------------------------------------------------- 201 | 202 | get_piece_length({dict, Dict}) -> 203 | {_, Info_dict} = dict:fetch(<<"info">>, Dict), 204 | dict:fetch(<<"piece length">>, Info_dict). 205 | 206 | %%-------------------------------------------------------------------- 207 | %% Function: get_length_and_name/1 208 | %% Purpose: Grab the length(s), the name(s), the 209 | %% length(s) of the task and the path where the 210 | %% file is going to be placed. 211 | %% Args: {dict, Dict}: The dictionary of the torrent 212 | %% file 213 | %% Returns: A tuple of the length(s), the name(s) of 214 | %% the file(s), the length(s) of the file(s) and the path 215 | %%-------------------------------------------------------------------- 216 | 217 | get_length_and_name({dict, Dict}) -> 218 | {_, Info_dict} = dict:fetch(<<"info">>, Dict), 219 | case dict:find(<<"files">>, Info_dict) of 220 | {ok, {_,Files_dict}} -> 221 | {get_length(Files_dict, 0), 222 | get_names(Files_dict), get_lengths_list(Files_dict), 223 | dict:fetch(<<"name">>, Info_dict)}; 224 | error -> 225 | Name_of_files = dict:fetch(<<"name">>, Info_dict), 226 | Length = dict:fetch(<<"length">>, Info_dict), 227 | {Length, 228 | [binary_to_list(Name_of_files)], [Length], ""} 229 | end. 230 | 231 | %%-------------------------------------------------------------------- 232 | %% Function: get_length/2 233 | %% Purpose: Backend to calculate the total length 234 | %% of a multiple file task. 235 | %% Args: List: of lengths 236 | %% Total: The total length for the task. 237 | %% Returns: The total length. 238 | %%-------------------------------------------------------------------- 239 | 240 | get_length([], Total) -> 241 | Total; 242 | get_length([{_,H}|T], Total) -> 243 | {ok, Value} = dict:find(<<"length">>, H), 244 | get_length(T, Total+Value). 245 | 246 | %%-------------------------------------------------------------------- 247 | %% Function: get_lengths_list/1 248 | %% Purpose: Returning the length(s) of the file(s) 249 | %% in a list. 250 | %% Args: List with length(s) 251 | %% Returns: The list with lengths 252 | %%-------------------------------------------------------------------- 253 | 254 | get_lengths_list([]) -> 255 | []; 256 | get_lengths_list([{_,H}|T]) -> 257 | {ok, Value} = dict:find(<<"length">>,H), 258 | [Value|get_lengths_list(T)]. 259 | 260 | %%-------------------------------------------------------------------- 261 | %% Function: get_names/1 262 | %% Purpose: Returning the names of the files 263 | %% Args: List with file names. 264 | %% Returns: The list with file names. 265 | %%-------------------------------------------------------------------- 266 | 267 | get_names([]) -> 268 | []; 269 | get_names([{_, H}|T]) -> 270 | {ok, {list, [Value]}} = dict:find(<<"path">>, H), 271 | [binary_to_list(Value)|get_names(T)]. 272 | 273 | %%-------------------------------------------------------------------- 274 | %% Function: handle_pieces/4 275 | %% Purpose: backend to grab the pieces from a list. 276 | %% Args: List: of pieces 277 | %% Piece_list: the new piece list 278 | %% Byte: Accumulator for calculating bytes. 279 | %% New_list: the new list of pieces 280 | %% Returns: A list of pieces. 281 | %%-------------------------------------------------------------------- 282 | 283 | handle_pieces([], Piece_list, _Byte, New_list) -> 284 | lists:reverse([lists:reverse(Piece_list)|New_list]); 285 | handle_pieces([H|T],Piece_list, Byte, New_list) when Byte =< 20 -> 286 | handle_pieces(T,[H|Piece_list], Byte+1, New_list); 287 | handle_pieces(List, Piece_list, _Byte, New_list) -> 288 | handle_pieces(List,[], 1, [lists:reverse(Piece_list)|New_list]). 289 | -------------------------------------------------------------------------------- /examples/download_manager.kju: -------------------------------------------------------------------------------- 1 | ###--------------------------------------------------------------------- 2 | ### Created by: Fredrik Gustafsson 3 | ### Creation date: 2011-10-18 4 | ### Refactored date: 2011-11-23 5 | ###--------------------------------------------------------------------- 6 | ### Description module downloading_manager 7 | ###--------------------------------------------------------------------- 8 | ### Parses the torrent file and handles the content 9 | ### which will be handed over to its children 10 | ###--------------------------------------------------------------------- 11 | ### Exports 12 | ###--------------------------------------------------------------------- 13 | ### start(.*torrent file name, Pid of gui module) 14 | ### spawns a new process running the init method and returns its pid. 15 | ###--------------------------------------------------------------------- 16 | ### init(.*torrent file name, Pid of gui module) 17 | ### initiates the information in the torrent file and 18 | ### spawns peers 19 | ###--------------------------------------------------------------------- 20 | ### is_valid_info_hash() 21 | ### checks if the info hash from peer is the same as 22 | ### ours. 23 | ###--------------------------------------------------------------------- 24 | ### get_my_id() 25 | ### Returns our id. 26 | ###--------------------------------------------------------------------- 27 | ### get_my_info_hash() 28 | ### Returns the info hash for the current task 29 | ###--------------------------------------------------------------------- 30 | ### get_info_clean() 31 | ### Returns the info hash in another clean format 32 | ###--------------------------------------------------------------------- 33 | 34 | export start/2 init/2 is_valid_info_hash/2 35 | get_my_id/1 get_my_info_hash/1 get_info_clean/1 36 | 37 | ## Functions exported for testing only commented further down 38 | export get_torrent_data/1 get_announce_list/1 get_pieces/1 39 | get_piece_length/1get_length_and_name/1 40 | 41 | start(File, GUIPid) -> 42 | spawn_link(?MODULE, init, [File, GUIPid]) 43 | 44 | init(File, GUIPid) -> 45 | process_flag(trap_exit, true) 46 | link(GUIPid) 47 | {dict, Dict} = get_torrent_data(File) 48 | Info_raw = dict:fetch(“info”, Dict) 49 | Info_bencoded = bencode:encode(Info_raw) 50 | Nr_of_pieces = length(get_pieces({dict, Dict})) 51 | Info_hash = list_to_binary(sha:sha1raw(Info_bencoded)) 52 | GUIPid ! {hash, {sha:sha1hash(Info_hash),Nr_of_pieces}} 53 | Peers_pid = peers:start(self(), get_announce_list({dict, Dict}), 54 | get_pieces({dict, Dict}), 55 | get_piece_length({dict, Dict}), 56 | get_length_and_name({dict, Dict}), File) 57 | link(Peers_pid) 58 | loop(Peers_pid, Info_hash,Info_bencoded, gui_basic:createUniqueId(), 59 | GUIPid, 0) 60 | 61 | is_valid_info_hash(Info_from_peer, Pid) -> 62 | Pid ! {valid_info, self(), Info_from_peer} 63 | receive 64 | Any -> Any end 65 | 66 | get_my_id(Dl_pid) -> 67 | Dl_pid ! {get_id, self()} 68 | receive 69 | {reply, Reply} -> 70 | Reply 71 | end 72 | 73 | get_my_info_hash(Dl_pid) -> 74 | Dl_pid ! {get_my_info_hash, self()} 75 | receive 76 | {reply, Reply} -> 77 | Reply 78 | end 79 | 80 | get_info_clean(Dl_pid) -> 81 | Dl_pid ! {get_clean_info, self()} 82 | receive 83 | {reply, Reply} -> 84 | Reply 85 | end 86 | 87 | ##-------------------------------------------------------------------- 88 | ## Function: loop/1 89 | ## Purpose: Sends information to GUI and corresponds 90 | ## to messages from its children. 91 | ## Args: Peers_pid: The pid of peers module 92 | ## Info_hash: Our info hash format 1 93 | ## Info_clean: Our info hash format 2 94 | ## My_id: Our unique id. 95 | ## GUI_pid : The pid of gui module 96 | ## Counter that checks so that we send a message 97 | ## only once to GUI. 98 | ## Returns:- 99 | ##-------------------------------------------------------------------- 100 | 101 | loop(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) -> 102 | receive 103 | {get_clean_info, From} -> 104 | From ! {reply, Info_clean} 105 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 106 | {valid_info, From, Info_from_peer} -> 107 | From ! binary_to_list(Info_hash) =:= Info_from_peer 108 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 109 | {get_id, From} -> 110 | From ! {reply, My_id} 111 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 112 | {get_my_info_hash, From} -> 113 | From ! {reply, Info_hash} 114 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 115 | {this_tracker, Tracker} -> 116 | GUI_pid ! {tracker, Tracker} 117 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 118 | {'EXIT', Peers_pid, _Reason} -> 119 | exit(self(), kill) 120 | stop -> ok 121 | after 3000 -> 122 | Peers_pid ! {get_downloaded, self()} 123 | receive 124 | {reply, {Downloaded, Current_pieces}} -> 125 | case Downloaded of 126 | 100 -> 127 | case Counter of 128 | 0 -> 129 | GUI_pid ! {percentage, {Downloaded, 130 | Current_pieces}} 131 | :(Peers_pid, Info_hash, Info_clean, 132 | My_id, GUI_pid, 1) 133 | _ -> 134 | ok 135 | end 136 | _ -> 137 | GUI_pid ! {percentage, {Downloaded, Current_pieces}} 138 | end 139 | end 140 | :(Peers_pid, Info_hash, Info_clean, My_id, GUI_pid, Counter) 141 | end 142 | 143 | ##-------------------------------------------------------------------- 144 | ## Function: get_torrent_data/1 145 | ## Purpose: decodes the torrent file 146 | ## Args: File: The file path to the torrent file 147 | ## Returns: Either, {dict, Dict} when the torrent file 148 | ## has been successfully read or {error, no_file} if 149 | ## the file cant be read. 150 | ##-------------------------------------------------------------------- 151 | 152 | get_torrent_data(File) -> 153 | case file:read_file(File) of 154 | {ok, Text} -> 155 | case bencode:decode(Text) of 156 | {{dict, Dict}, _Remainder} -> 157 | {dict, Dict} 158 | end 159 | _ -> 160 | {error, no_file} 161 | end 162 | 163 | ##-------------------------------------------------------------------- 164 | ## Function: get_announce_list/1 165 | ## Purpose: Returns the list of trackers in the 166 | ## announce list. 167 | ## Args: {dict, Dict}: The dictionary of the torrent 168 | ## file 169 | ## Returns: The list of trackers. 170 | ##-------------------------------------------------------------------- 171 | 172 | get_announce_list({dict, Dict}) -> 173 | case dict:find(“announce-list”, Dict) of 174 | {ok,{_, List}} -> 175 | List 176 | error -> 177 | {ok, Link} = dict:find(“announce”, Dict) 178 | [{ok, binary_to_list(Link)}] 179 | end 180 | 181 | ##-------------------------------------------------------------------- 182 | ## Function: get_pieces/1 183 | ## Purpose: Grabs the pieces from the dictionary 184 | ## Args: {dict, Dict}, the dictionary of the torrent file 185 | ## Returns: The list of the pieces 186 | ##-------------------------------------------------------------------- 187 | 188 | get_pieces({dict, Dict}) -> 189 | {_, Info_dict} = dict:fetch(“info”, Dict) 190 | Pieces = dict:fetch(“pieces”, Info_dict) 191 | handle_pieces(binary_to_list(Pieces),[], 1, []) 192 | 193 | ##-------------------------------------------------------------------- 194 | ## Function: get_piece_length/1 195 | ## Purpose: Grabs the piece length of the task from 196 | ## the dictionary of the torrent file. 197 | ## Args: {dict, Dict}, dictionary of the torrent file 198 | ## Returns: The piece length 199 | ##-------------------------------------------------------------------- 200 | 201 | get_piece_length({dict, Dict}) -> 202 | {_, Info_dict} = dict:fetch(“info”, Dict) 203 | dict:fetch(“piece length”, Info_dict) 204 | 205 | ##-------------------------------------------------------------------- 206 | ## Function: get_length_and_name/1 207 | ## Purpose: Grab the length(s), the name(s), the 208 | ## length(s) of the task and the path where the 209 | ## file is going to be placed. 210 | ## Args: {dict, Dict}: The dictionary of the torrent 211 | ## file 212 | ## Returns: A tuple of the length(s), the name(s) of 213 | ## the file(s), the length(s) of the file(s) and the path 214 | ##-------------------------------------------------------------------- 215 | 216 | get_length_and_name({dict, Dict}) -> 217 | {_, Info_dict} = dict:fetch(“info”, Dict) 218 | case dict:find(“files”, Info_dict) of 219 | {ok, {_,Files_dict}} -> 220 | {get_length(Files_dict, 0), 221 | get_names(Files_dict), get_lengths_list(Files_dict), 222 | dict:fetch(“name”, Info_dict)} 223 | error -> 224 | Name_of_files = dict:fetch(“name”, Info_dict) 225 | Length = dict:fetch(“length”, Info_dict) 226 | {Length, 227 | [binary_to_list(Name_of_files)], [Length], ""} 228 | end 229 | 230 | ##-------------------------------------------------------------------- 231 | ## Function: get_length/2 232 | ## Purpose: Backend to calculate the total length 233 | ## of a multiple file task. 234 | ## Args: List: of lengths 235 | ## Total: The total length for the task. 236 | ## Returns: The total length. 237 | ##-------------------------------------------------------------------- 238 | 239 | get_length([], Total) -> 240 | Total 241 | get_length([{_,H}|T], Total) -> 242 | {ok, Value} = dict:find(“length”, H) 243 | get_length(T, Total+Value) 244 | 245 | ##-------------------------------------------------------------------- 246 | ## Function: get_lengths_list/1 247 | ## Purpose: Returning the length(s) of the file(s) 248 | ## in a list. 249 | ## Args: List with length(s) 250 | ## Returns: The list with lengths 251 | ##-------------------------------------------------------------------- 252 | 253 | get_lengths_list([]) -> 254 | [] 255 | get_lengths_list([{_,H}|T]) -> 256 | {ok, Value} = dict:find(“length”,H) 257 | [Value|get_lengths_list(T)] 258 | 259 | ##-------------------------------------------------------------------- 260 | ## Function: get_names/1 261 | ## Purpose: Returning the names of the files 262 | ## Args: List with file names. 263 | ## Returns: The list with file names. 264 | ##-------------------------------------------------------------------- 265 | 266 | get_names([]) -> 267 | [] 268 | get_names([{_, H}|T]) -> 269 | {ok, {list, [Value]}} = dict:find(“path”, H) 270 | [binary_to_list(Value)|get_names(T)] 271 | 272 | ##-------------------------------------------------------------------- 273 | ## Function: handle_pieces/4 274 | ## Purpose: backend to grab the pieces from a list. 275 | ## Args: List: of pieces 276 | ## Piece_list: the new piece list 277 | ## Byte: Accumulator for calculating bytes. 278 | ## New_list: the new list of pieces 279 | ## Returns: A list of pieces. 280 | ##-------------------------------------------------------------------- 281 | 282 | handle_pieces([], Piece_list, _Byte, New_list) -> 283 | lists:reverse([lists:reverse(Piece_list)|New_list]) 284 | handle_pieces([H|T],Piece_list, Byte, New_list) when Byte ≤ 20 -> 285 | handle_pieces(T,[H|Piece_list], Byte+1, New_list) 286 | handle_pieces(List, Piece_list, _Byte, New_list) -> 287 | handle_pieces(List,[], 1, [lists:reverse(Piece_list)|New_list]) 288 | -------------------------------------------------------------------------------- /examples/etorrent_dht.erl: -------------------------------------------------------------------------------- 1 | %% @author Magnus Klaar 2 | %% @doc This module provides few helpers and supervise the DHT processes. 3 | %% Starts two workers: {@link etorrent_dht_state} and {@link etorrent_dht_net}. 4 | %% @end 5 | -module(etorrent_dht). 6 | -export([integer_id/1, 7 | list_id/1, 8 | random_id/0, 9 | closest_to/3, 10 | distance/2, 11 | find_self/0]). 12 | 13 | -type nodeinfo() :: etorrent_types:nodeinfo(). 14 | -type nodeid() :: etorrent_types:nodeid(). 15 | 16 | 17 | find_self() -> 18 | Self = etorrent_dht_state:node_id(), 19 | etorrent_dht_net:find_node_search(Self). 20 | 21 | -spec integer_id(list(byte()) | binary()) -> nodeid(). 22 | integer_id(<>) -> 23 | ID; 24 | integer_id(StrID) when is_list(StrID) -> 25 | integer_id(list_to_binary(StrID)). 26 | 27 | -spec list_id(nodeid()) -> list(byte()). 28 | list_id(ID) when is_integer(ID) -> 29 | binary_to_list(<>). 30 | 31 | -spec random_id() -> nodeid(). 32 | random_id() -> 33 | Byte = fun() -> random:uniform(256) - 1 end, 34 | Bytes = [Byte() || _ <- lists:seq(1, 20)], 35 | integer_id(Bytes). 36 | 37 | -spec closest_to(nodeid(), list(nodeinfo()), integer()) -> 38 | list(nodeinfo()). 39 | closest_to(InfoHash, NodeList, NumNodes) -> 40 | WithDist = [{distance(ID, InfoHash), ID, IP, Port} 41 | || {ID, IP, Port} <- NodeList], 42 | Sorted = lists:sort(WithDist), 43 | Limited = if 44 | (length(Sorted) =< NumNodes) -> Sorted; 45 | (length(Sorted) > NumNodes) -> 46 | {Head, _Tail} = lists:split(NumNodes, Sorted), 47 | Head 48 | end, 49 | [{NID, NIP, NPort} || {_, NID, NIP, NPort} <- Limited]. 50 | 51 | -spec distance(nodeid(), nodeid()) -> nodeid(). 52 | distance(BID0, BID1) when is_binary(BID0), is_binary(BID1) -> 53 | <> = BID0, 54 | <> = BID1, 55 | ID0 bxor ID1; 56 | distance(ID0, ID1) when is_integer(ID0), is_integer(ID1) -> 57 | ID0 bxor ID1. 58 | -------------------------------------------------------------------------------- /examples/etorrent_dht.kju: -------------------------------------------------------------------------------- 1 | ## @author Magnus Klaar 2 | ## @doc This module provides few helpers and supervise the DHT processes. 3 | ## Starts two workers: {@link etorrent_dht_state} and {@link etorrent_dht_net}. 4 | ## @end 5 | export 6 | integer_id/1 7 | list_id/1 8 | random_id/0 9 | closest_to/3 10 | distance/2 11 | find_self/0 12 | 13 | nodeinfo :: ‹etorrent_type:nodeinfo› # Note: ‘::’ says homogenous, ‘=’ says *is*. 14 | nodeid :: ‹etorrent_type:nodeid› 15 | 16 | find_self () = 17 | Self = etorrent_dht_state:node_id() 18 | etorrent_dht_net:find_node_search(Self) 19 | 20 | integer_id :: (‹binary›) -> ‹nodeid› 21 | integer_id (<>) = # Binary needs better syntax? 22 | ID. 23 | integer_id :: (‹list ‹byte››) -> ‹nodeid› # Still, beware of discontiguous funs. 24 | integer_id (StrID) = 25 | :(list_to_binary(StrID)) 26 | 27 | list_id :: (‹nodeid›) -> ‹list ‹byte›› 28 | list_id (ID) when is_integer(ID) = 29 | binary_to_list(<>) 30 | 31 | random_id :: () -> ‹nodeid› # ‘()’ *kinda* means unit. 32 | random_id () = 33 | Byte = fun () -> random:uniform(256) - 1 end 34 | Bytes = [Byte() | _ <- 1..20] 35 | integer_id(Bytes) 36 | 37 | closest_to :: (‹nodeid›, ‹list ‹nodeinfo››, ‹integer›) -> ‹list ‹nodeinfo›› 38 | closest_to (InfoHash, NodeList, NumNodes) = 39 | WithDist = [{distance(ID, InfoHash), ID, IP, Port} 40 | | {ID, IP, Port} <- NodeList] # Note: ‘<-’ desambiguates ‘|’ 41 | Sorted = lists:sort(WithDist) 42 | Limited = if length(Sorted) =< NumNodes 43 | Sorted 44 | else 45 | {Head, _Tail} = lists:split(NumNodes, Sorted) 46 | Head 47 | end 48 | [{NID, NIP, NPort} | {_, NID, NIP, NPort} <- Limited] 49 | 50 | distance :: (‹nodeid›, ‹nodeid›) -> ‹nodeid› 51 | distance (BID0, BID1) when is_binary(BID0 && is_binary(BID1) = 52 | <> = BID0 53 | <> = BID1 54 | ID0 bxor ID1 55 | distance (ID0, ID1) when is_integer(ID0) && is_integer(ID1) = 56 | ID0 bxor ID1 57 | -------------------------------------------------------------------------------- /examples/glab_fd.erl: -------------------------------------------------------------------------------- 1 | %%%------------------------------------------------------------------- 2 | %% @copyright Geoff Cant 3 | %% @author Geoff Cant 4 | %% @doc Implements (slightly?) unreliable failure detectors for the 5 | %% async bully algorithm used in gl_async_bully. 6 | %% @end 7 | %%%------------------------------------------------------------------- 8 | -module(glab_fd). 9 | 10 | %% API 11 | -export([new/1 12 | ,start/2 13 | ,stop/2 14 | ,filter_DOWN/2 15 | ,set/2 16 | ]). 17 | 18 | -record(fd, {name :: atom(), 19 | nodes = orddict:new() :: orddict:orddict() 20 | }). 21 | 22 | -opaque fd_set() :: #fd{}. 23 | 24 | -export_type([ fd_set/0 ]). 25 | 26 | %%==================================================================== 27 | %% API 28 | %%==================================================================== 29 | 30 | -spec new(atom()) -> fd_set(). 31 | new(Name) when is_atom(Name) -> 32 | #fd{name=Name}. 33 | 34 | -spec start(node(), fd_set()) -> fd_set(). 35 | start(Node, FD = #fd{name = Name, nodes = Nodes}) when is_atom(Node)-> 36 | case orddict:is_key(Node, Nodes) of 37 | true -> 38 | FD; 39 | false -> 40 | Ref = erlang:monitor(process, {Name, Node}), 41 | FD#fd{nodes=orddict:store(Node, Ref, Nodes)} 42 | end. 43 | 44 | -spec filter_DOWN({'DOWN', reference(), _, _, Info}, fd_set()) -> 45 | {'down', {atom(), node(), Info}, fd_set()} | 46 | {'ignore', Reason::atom(), fd_set()}. 47 | filter_DOWN({'DOWN', Ref, process, {Name, Node}, Info}, 48 | FD = #fd{name = Name, nodes = Nodes}) -> 49 | case orddict:find(Node, Nodes) of 50 | {ok, Ref} -> 51 | {down, {Name, Node, Info}, 52 | FD#fd{nodes=orddict:erase(Node, Nodes)}}; 53 | {ok, WrongRef} when is_reference(WrongRef) -> 54 | {ignore, stale, FD}; 55 | error -> 56 | {ignore, not_monitored, FD} 57 | end; 58 | filter_DOWN({'DOWN', _Ref, _Type, _Obj, _Info}, FD) -> 59 | {ignore, other_montior, FD}. 60 | 61 | 62 | -spec stop(node(), fd_set()) -> fd_set(). 63 | stop(Node, FD = #fd{nodes = Nodes}) when is_atom(Node) -> 64 | case orddict:find(Node, Nodes) of 65 | {ok, Ref} -> 66 | erlang:demonitor(Ref, [flush]), 67 | FD#fd{nodes=orddict:erase(Node, Nodes)}; 68 | error -> 69 | FD 70 | end. 71 | 72 | -spec set([node()], fd_set()) -> fd_set(). 73 | set(NewNodes, FD = #fd{nodes = Nodes}) -> 74 | lists:foldl(fun start/2, 75 | lists:foldl(fun stop/2, FD, 76 | orddict:fetch_keys(Nodes)), 77 | NewNodes). 78 | 79 | %%==================================================================== 80 | %% Internal functions 81 | %%==================================================================== 82 | -------------------------------------------------------------------------------- /examples/loop_swap.erl: -------------------------------------------------------------------------------- 1 | %% A process whose only job is to keep a counter. 2 | %% First version 3 | -module(loop_swap). 4 | -export([start/0, codeswitch/1]). 5 | 6 | start () -> 7 | loop(0). 8 | 9 | loop (Sum) -> 10 | receive 11 | {increment, Count} -> 12 | loop(Sum+Count); 13 | {counter, Pid} -> 14 | Pid ! {counter, Sum}, 15 | loop(Sum); 16 | code_switch -> 17 | ?MODULE:codeswitch(Sum) 18 | %% Force the use of 'codeswitch/1' from the latest MODULE version 19 | end. 20 | 21 | codeswitch (Sum) -> 22 | loop(Sum). 23 | -------------------------------------------------------------------------------- /examples/loop_swap.kju: -------------------------------------------------------------------------------- 1 | ## A process whose only job is to keep a counter. 2 | ## First version 3 | export 4 | start/0 5 | codeswitch/1 6 | 7 | start () = 8 | loop(0) 9 | 10 | loop (Sum) = 11 | receive 12 | {increment, Count} -> 13 | :(Sum + Count) 14 | {counter, Pid} -> 15 | Pid ! {counter, Sum} 16 | :(Sum) 17 | code_switch -> 18 | ?MODULE:codeswitch(Sum) 19 | end 20 | 21 | codeswitch /1 = 22 | fun loop/1 # ie. codeswitch(A) -> loop(A). Fun-Fundef notation. 23 | -------------------------------------------------------------------------------- /examples/maps1.er: -------------------------------------------------------------------------------- 1 | ## +export_all 2 | ## https://github.com/erlang/otp/blob/master/lib/dialyzer/test/small_SUITE_data/src/maps1.erl 3 | ## File: maps1.er 4 | ## Author: Björn-Egil Dahlberg 5 | ## Created: 2014-01-17 6 | ## 7 | 8 | 9 | 10 | 11 | 12 | export recv/3 decode/1 13 | 14 | #record can_pkt of {id, data :: binary(), timestamp} 15 | 16 | can_pkt() :: #{ id => term(), data => binary(), timestamp => term() } 17 | channel() :: atom() | pid() | {atom(),_} 18 | 19 | recv :: (<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R 20 | recv(Packet, Fun, Chan) -> 21 | #{id := Can_id, data := Can_data} = P = decode(Packet) 22 | Fun(P) 23 | 24 | decode :: (<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()} 25 | decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, 26 | Data:Len/binary, _/binary>>) -> 27 | #{id => Id, data => Data, timestamp => Timestamp} 28 | 29 | 30 | 31 | t1() -> 32 | #{bar=>fun t2/0} 33 | 34 | t2() -> ok 35 | 36 | map_state() :: #{ id => integer(), val => term() } 37 | 38 | update :: (map_state(), term()) -> map_state() 39 | 40 | update(#{ id := Id, val := Val } = M, X) when is_integer(Id) -> 41 | #{M val := [Val,X] } 42 | -------------------------------------------------------------------------------- /examples/maps1.erl: -------------------------------------------------------------------------------- 1 | %% https://github.com/erlang/otp/blob/master/lib/dialyzer/test/small_SUITE_data/src/maps1.erl 2 | %% File: maps1.erl 3 | %% Author: Björn-Egil Dahlberg 4 | %% Created: 2014-01-17 5 | %% 6 | 7 | -module(maps1). 8 | 9 | -compile([export_all]). 10 | 11 | 12 | -export([recv/3, decode/1]). 13 | 14 | %-record(can_pkt, {id, data :: binary(), timestamp}). 15 | 16 | -type can_pkt() :: #{ id => term(), data => binary(), timestamp => term() }. 17 | -type channel() :: atom() | pid() | {atom(),_}. 18 | 19 | -spec recv(<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R. 20 | recv(Packet, Fun, Chan) -> 21 | #{id := Can_id, data := Can_data} = P = decode(Packet), 22 | Fun(P). 23 | 24 | -spec decode(<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()}. 25 | decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, 26 | Data:Len/binary, _/binary>>) -> 27 | #{id => Id, data => Data, timestamp => Timestamp}. 28 | 29 | 30 | 31 | t1() -> 32 | #{bar=>fun t2/0}. 33 | 34 | t2() -> ok. 35 | 36 | -type map_state() :: #{ id => integer(), val => term() }. 37 | 38 | -spec update(map_state(), term()) -> map_state(). 39 | 40 | update(#{ id := Id, val := Val } = M, X) when is_integer(Id) -> 41 | M#{ val := [Val,X] }. 42 | -------------------------------------------------------------------------------- /examples/math.er: -------------------------------------------------------------------------------- 1 | ## Copyright © 2013 Pierre Fenoll ‹pierrefenoll@gmail.com› 2 | ## See LICENSE for licensing information. 3 | ## -*- er coding: utf-8 -*- 4 | module math 5 | 6 | ## math: some ubiquitous math functions. 7 | 8 | export fac/1 fib/1 9 | 10 | ## API 11 | 12 | fac (0) -> 1 13 | fac (N) when N > 0 && is_integer(N) -> 14 | N * :(N -1) 15 | 16 | fib (0) -> 0 17 | fib (1) -> 1 18 | fib (N) -> 19 | :(N -1) + :(N -2) 20 | 21 | ## Internals 22 | 23 | ## End of Module. 24 | -------------------------------------------------------------------------------- /examples/math.erl: -------------------------------------------------------------------------------- 1 | %% Copyright © 2013 Pierre Fenoll ‹pierrefenoll@gmail.com› 2 | %% See LICENSE for licensing information. 3 | %% -*- erlang coding: utf-8 -*- 4 | -module(math). 5 | 6 | %% math: some ubiquitous math functions. 7 | 8 | -export([fac/1, fib/1]). 9 | 10 | %% API 11 | 12 | fac (0) -> 1; 13 | fac (N) when N > 0, is_integer(N) -> 14 | N * fac(N -1). 15 | 16 | fib (0) -> 0; 17 | fib (1) -> 1; 18 | fib (N) -> 19 | fib(N -1) + fib(N -2). 20 | 21 | %% Internals 22 | 23 | %% End of Module. 24 | -------------------------------------------------------------------------------- /examples/non_tail_var.er: -------------------------------------------------------------------------------- 1 | ## Copyright © 2013 Pierre Fenoll ‹pierrefenoll@gmail.com› 2 | ## See LICENSE for licensing information. 3 | ## -*- coding: utf-8 -*- 4 | module non_tail_var 5 | compile export_all 6 | ## non_ta_tests: tests for module non_ta. 7 | 8 | #include_lib "eunit/include/eunit.hrl" 9 | 10 | 11 | ## API tests. 12 | 13 | f() -> 14 | M = true 15 | M # No parse errors 16 | false # over here… 17 | 18 | ## Internals 19 | 20 | ## End of Module. 21 | -------------------------------------------------------------------------------- /examples/non_tail_var.erl: -------------------------------------------------------------------------------- 1 | %% Copyright © 2013 Pierre Fenoll ‹pierrefenoll@gmail.com› 2 | %% See LICENSE for licensing information. 3 | %% -*- coding: utf-8 -*- 4 | -module(non_tail_var). 5 | -compile(export_all). 6 | %% non_ta_tests: tests for module non_ta. 7 | 8 | -include_lib("eunit/include/eunit.hrl"). 9 | 10 | 11 | %% API tests. 12 | 13 | f() -> 14 | M = true, 15 | M, 16 | false. 17 | 18 | %% Internals 19 | 20 | %% End of Module. 21 | -------------------------------------------------------------------------------- /examples/parse.er: -------------------------------------------------------------------------------- 1 | # -*- er, coding: utf-8 -*- 2 | ## +hipe +'{parse_transform,inline}' 3 | export string/1 4 | 5 | string :: (string()) → {ok | error, term()} 6 | string (Str) -> 7 | true = '':is_string(Str) 8 | {ok, Tokens} = xrl_scanner:string(Str) 9 | case yrl_parser:parse(Tokens) of 10 | {ok, Tree} → {ok, Tree} 11 | Error → {error, Error} # MAY print stuff 12 | end 13 | -------------------------------------------------------------------------------- /examples/parse.erl: -------------------------------------------------------------------------------- 1 | %% -*- erlang, coding: utf-8 -*- 2 | -module(parse). 3 | -compile({parse_transform,inline}). 4 | -export([string/1]). 5 | 6 | -spec string (Str::string()) -> {ok | error, term()}. 7 | string (Str) -> 8 | true = '':is_string(Str), 9 | {ok, Tokens} = xrl_scanner:string(Str), 10 | case yrl_parser:parse(Tokens) of 11 | {ok, Tree}=M0 -> M0; 12 | Error -> {error, Error} % MAY print stuff 13 | end. 14 | -------------------------------------------------------------------------------- /examples/parse.sh: -------------------------------------------------------------------------------- 1 | er --make parse.er 2 | 3 | erlc +hipe +debug_info parse.erl 4 | -------------------------------------------------------------------------------- /examples/pool.erl: -------------------------------------------------------------------------------- 1 | %% 2 | %% %CopyrightBegin% 3 | %% 4 | %% Copyright Ericsson AB 1996-2011. All Rights Reserved. 5 | %% 6 | %% The contents of this file are subject to the Erlang Public License, 7 | %% Version 1.1, (the "License"); you may not use this file except in 8 | %% compliance with the License. You should have received a copy of the 9 | %% Erlang Public License along with this software. If not, it can be 10 | %% retrieved online at http://www.erlang.org/. 11 | %% 12 | %% Software distributed under the License is distributed on an "AS IS" 13 | %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14 | %% the License for the specific language governing rights and limitations 15 | %% under the License. 16 | %% 17 | %% %CopyrightEnd% 18 | %% 19 | -module(pool). 20 | 21 | %% Supplies a computational pool of processors. 22 | %% The chief user interface function here is get_node() 23 | %% Which returns the name of the nodes in the pool 24 | %% with the least load !!!! 25 | %% This function is callable from any node including the master 26 | %% That is part of the pool 27 | %% nodes are scheduled on a per usgae basis and per load basis, 28 | %% Whenever we use a node, we put at the end of the queue, and whenever 29 | %% a node report a change in load, we insert it accordingly 30 | 31 | % User interface Exports ... 32 | -export([start/1, 33 | start/2, 34 | stop/0, 35 | get_nodes/0, 36 | get_nodes_and_load/0, 37 | get_node/0, 38 | pspawn/3, 39 | attach/1, 40 | pspawn_link/3]). 41 | 42 | %% Internal Exports 43 | -export([statistic_collector/0, 44 | do_spawn/4, 45 | init/1, 46 | handle_call/3, 47 | handle_cast/2, 48 | handle_info/2, 49 | terminate/2]). 50 | 51 | %% User interface 52 | 53 | %% Start up using the .hosts.erlang file 54 | 55 | -spec start(Name) -> Nodes when 56 | Name :: atom(), 57 | Nodes :: [node()]. 58 | start(Name) -> 59 | start(Name,[]). 60 | 61 | -spec start(Name, Args) -> Nodes when 62 | Name :: atom(), 63 | Args :: string(), 64 | Nodes :: [node()]. 65 | start(Name, Args) when is_atom(Name) -> 66 | gen_server:start({global, pool_master}, pool, [], []), 67 | Hosts = net_adm:host_file(), 68 | Nodes = start_nodes(Hosts, Name, Args), 69 | lists:foreach(fun attach/1, Nodes), 70 | Nodes. 71 | 72 | %% 73 | %% Interface functions ... 74 | %% 75 | -spec get_nodes() -> [node()]. 76 | get_nodes() -> 77 | get_elements(2, get_nodes_and_load()). 78 | 79 | -spec attach(Node) -> 'already_attached' | 'attached' when 80 | Node :: node(). 81 | attach(Node) -> 82 | gen_server:call({global, pool_master}, {attach, Node}). 83 | 84 | get_nodes_and_load() -> 85 | gen_server:call({global, pool_master}, get_nodes). 86 | 87 | -spec get_node() -> node(). 88 | get_node() -> 89 | gen_server:call({global, pool_master}, get_node). 90 | 91 | -spec pspawn(Mod, Fun, Args) -> pid() when 92 | Mod :: module(), 93 | Fun :: atom(), 94 | Args :: [term()]. 95 | pspawn(M, F, A) -> 96 | gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}). 97 | 98 | -spec pspawn_link(Mod, Fun, Args) -> pid() when 99 | Mod :: module(), 100 | Fun :: atom(), 101 | Args :: [term()]. 102 | pspawn_link(M, F, A) -> 103 | P = pspawn(M, F, A), 104 | link(P), 105 | P. 106 | 107 | start_nodes([], _, _) -> []; 108 | start_nodes([Host|Tail], Name, Args) -> 109 | case slave:start(Host, Name, Args) of 110 | {error, {already_running, Node}} -> 111 | io:format("Can't start node on host ~w due to ~w~n",[Host, {already_running, Node}]), 112 | [Node | start_nodes(Tail, Name, Args)]; 113 | {error, R} -> 114 | io:format("Can't start node on host ~w due to ~w~n",[Host, R]), 115 | start_nodes(Tail, Name, Args); 116 | {ok, Node} -> 117 | [Node | start_nodes(Tail, Name, Args)] 118 | end. 119 | 120 | -spec stop() -> 'stopped'. 121 | stop() -> 122 | gen_server:call({global, pool_master}, stop). 123 | 124 | get_elements(_Pos,[]) -> []; 125 | get_elements(Pos,[E|T]) -> [element(Pos,E) | get_elements(Pos,T)]. 126 | 127 | stop_em([]) -> stopped; 128 | stop_em([N|Tail]) -> 129 | rpc:cast(N, erlang, halt, []), 130 | stop_em(Tail). 131 | 132 | init([]) -> 133 | process_flag(trap_exit, true), 134 | spawn_link(pool, statistic_collector, []), 135 | {ok,[{0,node()}]}. 136 | 137 | handle_call(get_nodes, _From, Nodes)-> 138 | {reply, Nodes, Nodes}; 139 | handle_call(get_node, _From, [{Load,N}|Tail]) -> 140 | {reply, N, Tail++[{Load+1, N}]}; 141 | handle_call({attach, Node}, _From, Nodes) -> 142 | case lists:keymember(Node, 2, Nodes) of 143 | true -> 144 | {reply, already_attached, Nodes}; 145 | false -> 146 | erlang:monitor_node(Node, true), 147 | spawn_link(Node, pool, statistic_collector, []), 148 | {reply, attached, Nodes++[{999999,Node}]} 149 | end; 150 | handle_call({spawn, Gl, M, F, A}, _From, Nodes) -> 151 | [{Load,N}|Tail] = Nodes, 152 | Pid = spawn(N, pool, do_spawn, [Gl, M, F, A]), 153 | {reply, Pid, Tail++[{Load+1, N}]}; 154 | handle_call(stop, _From, Nodes) -> 155 | %% clean up in terminate/2 156 | {stop, normal, stopped, Nodes}. 157 | 158 | handle_cast(_, Nodes) -> 159 | {noreply, Nodes}. 160 | 161 | handle_info({Node,load,Load}, Nodes) -> 162 | Nodes2 = insert_node({Load,Node}, Nodes), 163 | {noreply, Nodes2}; 164 | handle_info({nodedown, Node}, Nodes) -> 165 | {noreply, lists:keydelete(Node, 2, Nodes)}; 166 | handle_info(_, Nodes) -> %% The EXIT signals etc.etc 167 | {noreply, Nodes}. 168 | 169 | terminate(_Reason, Nodes) -> 170 | N = lists:delete(node(), get_elements(2, Nodes)), 171 | stop_em(N), 172 | ok. 173 | 174 | -spec do_spawn(pid(), module(), atom(), [term()]) -> term(). 175 | do_spawn(Gl, M, F, A) -> 176 | group_leader(Gl, self()), 177 | apply(M, F, A). 178 | 179 | insert_node({Load,Node},[{L,Node}|Tail]) when Load > L -> 180 | %% We have a raised load here 181 | pure_insert({Load,Node},Tail); 182 | insert_node({Load,Node},[{L,N}|Tail]) when Load =< L -> 183 | %% Move forward in the list 184 | T = lists:keydelete(Node,2,[{L,N}|Tail]), 185 | [{Load,Node} | T]; 186 | insert_node(Ln,[H|T]) -> 187 | [H | insert_node(Ln,T)]; 188 | insert_node(X,[]) -> % Can't happen 189 | error_logger:error_msg("Pool_master: Bad node list X=~w\n", [X]), 190 | exit(crash). 191 | 192 | pure_insert({Load,Node},[]) -> 193 | [{Load,Node}]; 194 | pure_insert({Load,Node},[{L,N}|Tail]) when Load < L -> 195 | [{Load,Node}, {L,N} | Tail]; 196 | pure_insert(L,[H|T]) -> [H|pure_insert(L,T)]. 197 | 198 | %% Really should not measure the contributions from 199 | %% the back ground processes here .... which we do :-( 200 | %% We don't have to monitor the master, since we're slaves anyway 201 | 202 | statistic_collector() -> 203 | statistic_collector(5). 204 | 205 | statistic_collector(0) -> exit(normal); 206 | statistic_collector(I) -> 207 | sleep(300), 208 | case global:whereis_name(pool_master) of 209 | undefined -> 210 | statistic_collector(I-1); 211 | M -> 212 | stat_loop(M, 999999) 213 | end. 214 | 215 | %% Do not tell the master about our load if it has not changed 216 | 217 | stat_loop(M, Old) -> 218 | sleep(2000), 219 | case statistics(run_queue) of 220 | Old -> 221 | stat_loop(M, Old); 222 | NewLoad -> 223 | M ! {node(), load, NewLoad}, %% async 224 | stat_loop(M, NewLoad) 225 | end. 226 | 227 | sleep(I) -> receive after I -> ok end. 228 | -------------------------------------------------------------------------------- /examples/pool.kju: -------------------------------------------------------------------------------- 1 | ## Supplies a computational pool of processors. 2 | ## The chief user interface function here is get_node() 3 | ## Which returns the name of the nodes in the pool 4 | ## with the least load !!!! 5 | ## This function is callable from any node including the master 6 | ## That is part of the pool 7 | ## nodes are scheduled on a per usgae basis and per load basis, 8 | ## Whenever we use a node, we put at the end of the queue, and whenever 9 | ## a node report a change in load, we insert it accordingly 10 | 11 | # User interface Exports ... 12 | export 13 | start/1,2 14 | stop/0 15 | get_nodes/0 16 | get_nodes_and_load/0 17 | get_node/0 18 | pspawn/3 19 | attach/1 20 | pspawn_link/3 21 | 22 | ## Internal Exports 23 | export 24 | statistic_collector/0 25 | do_spawn/4 26 | init/1 27 | handle_call/3,2 28 | handle_info/2 29 | terminate/2 30 | 31 | ## User interface 32 | 33 | ## Start up using the .hosts.erlang file 34 | 35 | start (Name) -> Nodes when 36 | Names :: Nodes :: [] 37 | start (Name) = 38 | start(Name,[]) 39 | 40 | start (Name, Args) -> Nodes when 41 | Name :: 42 | Args :: 43 | Nodes :: [] 44 | start (Name, Args) = 45 | gen_server:start({global, pool_master}, pool, [], []) 46 | Hosts = net_adm:host_file() 47 | Nodes = start_nodes(Hosts, Name, Args) 48 | lists:foreach(fun attach/1, Nodes) 49 | Nodes 50 | 51 | ## 52 | ## Interface functions ... 53 | ## 54 | get_nodes () -> [] 55 | get_nodes () = 56 | get_elements(2, get_nodes_and_load()) 57 | 58 | attach (Node) -> 'already_attached' | 'attached' when 59 | Node :: 60 | attach (Node) = 61 | gen_server:call({global, pool_master}, {attach, Node}) 62 | 63 | get_nodes_and_load () = 64 | gen_server:call({global, pool_master}, get_nodes) 65 | 66 | get_node () -> 67 | get_node () = 68 | gen_server:call({global, pool_master}, get_node) 69 | 70 | pspawn (Mod, Fun, Args) -> when 71 | Mod :: 72 | Fun :: 73 | Args :: [] 74 | pspawn (M, F, A) = 75 | gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}) 76 | 77 | pspawn_link (Mod, Fun, Args) -> when 78 | Mod :: 79 | Fun :: 80 | Args :: [] 81 | pspawn_link (M, F, A) = 82 | P = pspawn(M, F, A) 83 | link(P) 84 | P 85 | 86 | start_nodes ([], _, _) = [] 87 | start_nodes ([Host|Tail], Name, Args) = 88 | case slave:start(Host, Name, Args) of 89 | {error, {already_running, Node}} -> 90 | io:format("Can't start node on host ~w due to ~w~n",[Host, {already_running, Node}]) 91 | [Node | start_nodes(Tail, Name, Args)] 92 | {error, R} -> 93 | io:format("Can't start node on host ~w due to ~w~n",[Host, R]) 94 | start_nodes(Tail, Name, Args) 95 | {ok, Node} -> 96 | [Node | start_nodes(Tail, Name, Args)] 97 | end 98 | 99 | stop () -> 'stopped' 100 | stop () = 101 | gen_server:call({global, pool_master}, stop) 102 | 103 | get_elements (_Pos,[]) = [] 104 | get_elements (Pos,[E|T]) = [element(Pos,E) | get_elements(Pos,T)] 105 | 106 | stop_em ([]) = stopped 107 | stop_em ([N|Tail]) = 108 | rpc:cast(N, erlang, halt, []) 109 | stop_em(Tail) 110 | 111 | init ([]) = 112 | process_flag(trap_exit, true) 113 | spawn_link(pool, statistic_collector, []) 114 | {ok,[{0,node()}]} 115 | 116 | handle_call (get_nodes, _From, Nodes)= 117 | {reply, Nodes, Nodes} 118 | handle_call (get_node, _From, [{Load,N}|Tail]) = 119 | {reply, N, Tail++[{Load+1, N}]} 120 | handle_call ({attach, Node}, _From, Nodes) = 121 | if lists:keymember(Node, 2, Nodes) 122 | {reply, already_attached, Nodes} 123 | else 124 | erlang:monitor_node(Node, true) 125 | spawn_link(Node, pool, statistic_collector, []) 126 | {reply, attached, Nodes++[{999999,Node}]} 127 | end 128 | handle_call ({spawn, Gl, M, F, A}, _From, Nodes) = 129 | [{Load,N}|Tail] = Nodes 130 | Pid = spawn(N, pool, do_spawn, [Gl, M, F, A]) 131 | {reply, Pid, Tail++[{Load+1, N}]} 132 | handle_call (stop, _From, Nodes) = 133 | ## clean up in terminate/2 134 | {stop, normal, stopped, Nodes} 135 | 136 | handle_cast (_, Nodes) = 137 | {noreply, Nodes} 138 | 139 | handle_info ({Node,load,Load}, Nodes) = 140 | Nodes2 = insert_node({Load,Node}, Nodes) 141 | {noreply, Nodes2} 142 | handle_info ({nodedown, Node}, Nodes) = 143 | {noreply, lists:keydelete(Node, 2, Nodes)} 144 | handle_info (_, Nodes) = ## The EXIT signals etc.etc 145 | {noreply, Nodes} 146 | 147 | terminate (_Reason, Nodes) = 148 | N = lists:delete(node(), get_elements(2, Nodes)) 149 | stop_em(N) 150 | ok 151 | 152 | do_spawn (, , , []) -> 153 | do_spawn (Gl, M, F, A) = 154 | group_leader(Gl, self()) 155 | apply(M, F, A) 156 | 157 | insert_node ({Load,Node},[{L,Node}|Tail]) when Load > L = 158 | ## We have a raised load here 159 | pure_insert({Load,Node},Tail) 160 | insert_node ({Load,Node},[{L,N}|Tail]) when Load ≤ L = 161 | ## Move forward in the list 162 | T = lists:keydelete(Node,2,[{L,N}|Tail]) 163 | [{Load,Node} | T] 164 | insert_node (Ln,[H|T]) = 165 | [H | insert_node(Ln,T)] 166 | insert_node (X,[]) = # Can't happen 167 | error_logger:error_msg("Pool_master: Bad node list X=~w\n", [X]) 168 | exit(crash) 169 | 170 | pure_insert ({Load,Node},[]) = 171 | [{Load,Node}] 172 | pure_insert ({Load,Node},[{L,N}|Tail]) when Load < L = 173 | [{Load,Node}, {L,N} | Tail] 174 | pure_insert (L,[H|T]) -> [H|pure_insert(L,T)] 175 | 176 | ## Really should not measure the contributions from 177 | ## the back ground processes here .... which we do :-( 178 | ## We don't have to monitor the master, since we're slaves anyway 179 | 180 | statistic_collector () = 181 | statistic_collector(5) 182 | 183 | statistic_collector (0) = exit(normal) 184 | statistic_collector (I) = 185 | sleep(300) 186 | case global:whereis_name(pool_master) of 187 | undefined -> 188 | statistic_collector(I-1) 189 | M -> 190 | stat_loop(M, 999999) 191 | end 192 | 193 | ## Do not tell the master about our load if it has not changed 194 | 195 | stat_loop (M, Old) = 196 | sleep(2000) 197 | case statistics(run_queue) of 198 | Old -> 199 | stat_loop(M, Old) 200 | NewLoad -> 201 | M ! {node(), load, NewLoad} ## async 202 | stat_loop(M, NewLoad) 203 | end 204 | 205 | sleep (I) = receive after I -> ok end 206 | -------------------------------------------------------------------------------- /examples/propfolding.erl: -------------------------------------------------------------------------------- 1 | -module(propfolding). 2 | -export([get/1]). 3 | 4 | get (Doc) -> 5 | {Stars, AreaID, CheckinDate, HotelID, Rooms} = 6 | lists:foldl( 7 | fun 8 | ({<<"stars">>, Stars}, Acc) -> setelement(1, Acc, Stars); 9 | ({<<"areaID">>, AreaID}, Acc) -> setelement(2, Acc, AreaID); 10 | ({<<"checkinDate">>, <>}, Acc) -> 11 | setelement(3, Acc, binary_to_integer(<>)); 12 | ({<<"hotelID">>, HotelID}, Acc) -> setelement(4, Acc, HotelID); 13 | ({<<"rooms">>, Rooms}, Acc) -> setelement(5, Acc, Rooms) 14 | end, 15 | {undef, undef, undef, undef, undef}, 16 | Doc), 17 | %% blablabla 18 | ok. 19 | -------------------------------------------------------------------------------- /examples/propfolding.kju: -------------------------------------------------------------------------------- 1 | ## propfolding 2 | export get/1 3 | 4 | get (Doc) = 5 | {Stars, AreaID, CheckinDate, HotelID, Rooms} = 6 | lists:foldl( 7 | fun 8 | ({“stars”, Stars}, Acc) -> setelement(1, Acc, Stars) 9 | ({“areaID”, AreaID}, Acc) -> setelement(2, Acc, AreaID) 10 | ({“checkinDate”, <>}, Acc) -> 11 | setelement(3, Acc, binary_to_integer(<>)) 12 | ({“hotelID”, HotelID}, Acc) -> setelement(4, Acc, HotelID) 13 | ({“rooms”, Rooms}, Acc) -> setelement(5, Acc, Rooms) 14 | end, 15 | {undef, undef, undef, undef, undef}, 16 | Doc) 17 | ## blablabla 18 | ok 19 | -------------------------------------------------------------------------------- /examples/snippets.er: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ## Add snippets at EoF 3 | ## Separate them with \n\n down to the last one. 4 | f()-> ok 5 | 6 | len (L) when is_list(L) -> length(L) 7 | len (T) when is_tuple(T) -> size(T) 8 | 9 | f()-> thiS() W=ill tot:ally(W + ork) 10 | g()-> i_ll(give) my:hand() to_cut 11 | 12 | length ([]) -> 0 13 | length ([_|T]) -> 1 + :(T) # ‘:’ references the currently defined function, here: ‘length’ 14 | 15 | #5 16 | f () -> 17 | case orddict:find(Node, Nodes) of 18 | {ok, Ref} -> {down, node, Ref} 19 | {ok, WrongRef} when is_reference(WrongRef) -> 'not the one' 20 | error -> {ignore, not_monitored, FD} 21 | end 22 | 23 | 'f' () -> 24 | receive 25 | # {'ok', Ref} -> 'do_somth'('arg1', 2) {'down', 'node', Ref} 26 | {'ok', WrongRef} when 'is_reference'(WrongRef) -> 'smth'() 'not the one' 27 | 'error' -> {'ignore', 'not_monitored', FD} 28 | end 29 | 30 | t () -> 31 | try Expr 32 | catch 33 | throw:Term -> Term 34 | exit:Reason -> {'EXIT',Reason} 35 | error:Reason -> {'EXIT',{Reason,erlang:get_stacktrace()}} 36 | end 37 | 38 | i (Arg1) -> 39 | cond not Arg1 -> false 40 | true -> false end 41 | 42 | len (A) -> 43 | case A of 44 | L when is_list(L) -> 45 | length(L) 46 | T when is_tuple(T) -> 47 | size(T) 48 | end 49 | 50 | #10 51 | p (Str) -> 52 | F = fun io:format("~p\n")/1 53 | (F(Str)) 54 | 55 | b () -> 56 | begin 57 | do() 58 | some() 59 | stuff 60 | end 61 | 62 | 'p&to_list' (Arg) -> 63 | case Arg of 64 | T when is_tuple(T) -> [p(X) X | X <~ T] 65 | L when is_list(L) -> [p(X) X | X <- L] 66 | B when is_binary(B) -> [p(X) X | X <= B] 67 | end 68 | 69 | map (F, A) when is_tuple(A) -> {(F(X)) | X <~ A} 70 | 71 | try…catch () -> 72 | try bla() Expression of 73 | SuccessfulPattern1 -> 74 | Expression1 75 | SuccessfulPattern2 -> 76 | Expression2 77 | catch 78 | TypeOfError:ExceptionPattern1 -> 79 | Expression3 80 | TypeOfError:ExceptionPattern2 -> 81 | Expression4 82 | end 83 | 84 | #15 85 | π () -> 3.1415926535 86 | 87 | brilliant! () -> << case X of Z when m(Z) -> 0 Y -> 1 end | X <~ _>> 88 | 89 | i+am+impressed () -> 90 | work() 91 | try 92 | talk() 93 | _Knight = "None shall Pass!" 94 | _Doubles = [2*N | N <- [1..100]] 95 | throw(up) 96 | _WillReturnThis = tequila 97 | catch 98 | Exception:Reason -> {caught, Exception, Reason} 99 | end 100 | 101 | export a/0 b/1 c/234 102 | 103 | f()-> {2..3} <<-6..18>> [Min..g()-2] 104 | 105 | #20 106 | f()-> [1|[2|[3|[]]]] 107 | 108 | f()-> fun (Thu) -> gaim end 109 | 110 | many_args (A, [H|T], "str", -2) -> ok 111 | 112 | fun_clauses () -> 113 | fun (123) -> "123" 114 | (231) -> 'deux-trois-un' 115 | (stop) -> 116 | bla(1, 2, B, [$*]) 117 | babebibobu() 118 | case do() of 119 | hammer -> lel() so_funny 120 | _ -> something 121 | end 122 | end 123 | 124 | f() -> case asd of a -> a s -> smth (d) -> ah+ah! end 125 | 126 | #25 127 | len :: (list()) -> pos_integer() | 0 128 | len (L) when is_list(L) -> length(L) 129 | 130 | len :: (list()) -> pos_integer() 131 | len (L) -> length(L) 132 | # len :: (tuple()) -> pos_integer() 133 | len (T) when is_tuple(T) -> size(T) 134 | 135 | '!' (0) -> 1 136 | '!' :: (N) -> M when N::pos_integer() M::pos_integer() 137 | '!' (N) -> N * :(N - 1) 138 | 139 | '!' :: () -> fun((N) -> N) when N::pos_integer() 140 | '!' () -> 141 | fun (0) -> 1 142 | (N) when N > 0 -> 143 | N * :(N - 1) # ‘:’ also works on funs. 144 | end 145 | 146 | flat.map (Fun, List) -> 147 | ((fun append/1 . fun map/2) (Fun, List)) 148 | 149 | #30 150 | flatmap /2 -> 151 | fun append/1 . fun map/2 152 | 153 | f/1 -> 154 | fun (0) -> 1 155 | (N) -> N * :(N - 1) 156 | end 157 | 158 | f()-> [ (a()):loop() ] 159 | 160 | f()-> A = B = C = d 161 | 162 | zip :: ([term()], [term()]) -> [{term(),term()}] 163 | ## <=> zip/2 :: lists:zip/2 164 | zip ([X|Xs], [Y|Ys]) -> 165 | [{X',Y'} | X' <- [X|Xs] 166 | | Y' <- [Y|Ys]] 167 | # <=> [{X,Y} | :(Xs, Ys)] 168 | zip ([], []) -> [] # Using a list comprehension implementation, this line is useless. 169 | # However, it shows that the spec only applies to the first fun_clause. 170 | # However, a warning should be thrown as the second fun_clause is useless. 171 | 172 | #35 173 | fizz_buzz (N) -> 174 | [case {X rem 3, X rem 5} of 175 | {0, 0} -> fizzbuzz 176 | {0, _} -> fizz 177 | {_, 0} -> buzz 178 | {_, _} -> X 179 | end | X <- [1..N]] 180 | 181 | f()-> string:sub_string(OptStr, P, P + 2) >= "3.4" 182 | 183 | export start/1,2 184 | 185 | verify_int (Int, Max) -> 186 | false || verify_strict_int(Int) && verify_strict_int(Max) && Int =< Max 187 | 188 | factorial /1 -> 189 | fun 190 | (f ) -> fun :/1 191 | (ff) -> fun :()/1 192 | (0) -> 1 193 | (N) when N > 0 -> N * :(N -1) 194 | (f24) -> fun :(4)/0 195 | (___) -> :(4) 196 | end 197 | 198 | #40 199 | f()-> fun io:format("~p\n")/1 ([dg]) /1 # fun declaration then division of ([dg]) 200 | f()-> ((fun io:format("~p\n")/1)([dg]))/1 # fun declaration then application to [dg] then division 201 | 202 | f()-> fun (fun io:format("~p\n")/1)([dg])/0 # 2 fun declarations, no division 203 | ## Will fail at runtime as a cannot describe a function's name 204 | 205 | import map/2 foreach/2 of lists 206 | 207 | f()-> # Integers 208 | A = 10 209 | B = -234 210 | C = 16#AB10F 211 | D = 2#110111010 212 | E = $A 213 | # Floats 214 | F = 17.368 215 | G = -56.654 216 | H = 12.34E-10 217 | 218 | f()-> ok = a:b() 219 | 220 | #45 221 | ## Depends on 2 versions of same package: 222 | import master of "github.com/anon42/app1" 223 | import v1.2 of "github.com/anon42/app1" 224 | import master of "bitbucket.org/user2/repo2" 225 | export main/0 226 | main () -> 227 | ok = app2:bla(bla, bla) 228 | app1:master:app1:f() 229 | app1:v1.2 :app1:f() 230 | 231 | f('$char') -> $c 232 | 233 | f()-> # _ separating digits 234 | 1_000 235 | 1_23_777 236 | 1_00#9_999_957 237 | 10.000_001E-2_345 238 | 239 | f :: () -> <<_:Integer, _:_*OtherInteger>> 240 | f()-> <<42, 3:2, g():(h()), 1+2:3/ba-be-bi>> 241 | 242 | # my_other_function :: (‹names›, ‹places›) → ‹atom› abandoned notation 243 | my_other_function :: (names(), places()) → atom() 244 | my_other_function (Names, Places) -> 245 | R1 = ø(Ø, Ça, Names) 246 | R2 = µ(Ð, Æ, Places) 247 | if R1 ≥ R2 -> ok if not -> ko 248 | 249 | #50 250 | # There is only 1 (L)Comprehension here. 251 | f()-> [[log(E) | E <- LogVars] | DebugOn] 252 | 253 | # http://www.erlang.org/doc/reference_manual/expressions.html#id77958 254 | f({connect,_,To,_,_} = Signal, To, 1+2 bxor 3 = 0) -> 255 | S = "prefixed string" # Note: static string concatenation not supported 256 | "prefix"++Str = S 257 | [$p,$r,$e,$f,$i,$x|_] = S 258 | 259 | b() :: true | false 260 | h :: (b(), b()) -> b() 261 | h (A, B) -> A band B 262 | 263 | red_panda_food() :: bamboo | birds | eggs | berries 264 | squid_food() :: sperm_whale 265 | feed(A) :: fun(() -> A) 266 | feeder :: (red_panda) -> feed(red_panda_food()) 267 | feeder (red_panda)-> fun () -> bamboo end 268 | feeder :: (squid) -> feed( squid_food()) 269 | feeder (squid) -> fun () -> sperm_whale end 270 | 271 | 'key-value-stores'() -> 272 | ## Neutral 273 | M' = #{} 274 | R' = #{r} 275 | ## Creation 276 | M0 = #{k => v} 277 | R0 = #{r k = v} 278 | ## Update 279 | M1 = #{M0 k := v2} 280 | R1 = #{R0 r k = v2} 281 | ## Read 282 | V = #{M1 k} 283 | V = #{R1 r k} 284 | ## Matching 285 | #{k := V} = M1 286 | #{r k = V} = R1 287 | 288 | #55 289 | nesting() -> 290 | ## Records 291 | R = #{person name= #{name first="Robert",last="Virding"}, phone=120+3} 292 | First = #{#{R person name} name first} 293 | ## Maps 294 | M = #{name=> #{first=>"Robert",last=>"Virding"}, phone=>120+3} 295 | First = #{#{M name} first} 296 | 297 | f()-> {a} {a=b} {a=b, c=d} {b, c=d} # 4 tuples 298 | 299 | some_more () -> 300 | #{"favorite" := Animal, Animal := Name} = #{Pets "favorite" := "dog"} 301 | FoggyPlaces = [X | X := fog <- #{montreal=>storms, london=>fog, paris=>sun}] 302 | #{boston := foggy, london := foggy} = #{X => foggy | X <- [london,boston]} 303 | # X = M#{c}#{y} is #{#{M c} y} 304 | NPants = #{pants _ = undef} 305 | 306 | note () -> 307 | M = #{A B => true} # Updates A 308 | M' = #{A > B => true} # New map with boolean key 309 | M'' = #{g() A > B => yo()} # Permissive syntax! 310 | 311 | record name of {first :: string(), last :: string()} # Also creates type '#{name}' 312 | name() :: #{name} 313 | # export_bis name/0 # Now that name is an exportable type… 314 | record person of {name::name(), phone::pos_integer()} 315 | record_def_and_usage () -> 316 | R' = #{name first="Robert", last="Virding"} 317 | R = #{person name=R', phone=123} 318 | First = #{#{R person name} name first} 319 | 320 | #60 321 | f() -> # These lines are the same 322 | R = a :g() 323 | R = (a):g() 324 | R = a:g() 325 | 326 | f() -> {"bla\nbla\"bla", “bla\nbla\”bla”} 327 | <<"Hello", $\s, "World!">> = “Hello World!” 328 | 329 | decode :: (<<_:64,_:_*8>>) -> #{id => <<_:11>>,timestamp => char()} 330 | decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, 331 | Data:Len/binary, _/binary>>) -> 332 | #{id => Id, data => Data, timestamp => Timestamp} 333 | 334 | ty_long_var_assoc() :: f(T,Q,W) when T::Q::W::text() 335 | processor() :: fun((T) -> T) when T::text() 336 | ## 337 | analyze :: ([#{document}], [processor()]) -> [#{tokenized_document}] 338 | analyze (Documents, Processors) -> 339 | plists:map( # Done in parallel 340 | fun (Document) -> 341 | Words = tokenize(#{Document document text}, “”, []) 342 | #{tokenized_document 343 | url = #{Document document url} 344 | , words = [apply_processors(Processors,Word) | Word<-Words] } 345 | end, Documents) 346 | ## 347 | normalizer/1 :: processor() 348 | normalizer (Word) -> 349 | processor(fun string:to_lower/1, Word) 350 | ## 351 | processor :: (processor(), T) -> T when T::text() 352 | processor (Fun, Binary) -> 353 | << <> = Element 354 | <<(Fun(E))>> 355 | | <> <= Binary>> 356 | 357 | record conf 358 | of { dirs = [] 359 | , destination = filename:join(cwd(), "docs/erldocs") 360 | , includes = [ cwd() ++ "/include" 361 | , cwd() ] 362 | , logfile ∷ string() | binary() 363 | } 364 | 365 | #65 366 | f()-> if if not a a if not -> b 367 | -> "1" 368 | if not 369 | -> 2*b 370 | 371 | w()-> #{ key => C = 1 + 2 } 372 | 373 | module some_attributes 374 | behaviour gen_server 375 | ## 376 | export start_link/0 377 | export init/1 378 | handle_call/3 379 | handle_cast/2 380 | handle_info/2 381 | terminate/2 382 | code_change/3 383 | record state of {} 384 | ## 385 | start_link () -> 386 | gen_server:start_link({local,'?MODULE'}, '?MODULE', [], []) 387 | ## 388 | init ([]) -> 389 | {ok, #{state}} 390 | handle_call (_Request, _From, State) -> 391 | {reply, ignored, State} 392 | handle_cast (_Msg, State) -> 393 | {noreply, State} 394 | handle_info (_Info, State) -> 395 | {noreply, State} 396 | terminate (_Reason, _State) -> 397 | ok 398 | code_change (_OldVsn, State, _Extra) -> 399 | {ok, State} 400 | 401 | count (Items) -> 402 | R = Items |> fun sum/1 403 | # |> integer_to_list(^) # (Just an old idea…) 404 | |> fun [_]/1 405 | |> fun io:format("~p\n")/1 406 | ok = R ## Matches 407 | # 408 | # This block is just leftover ideas on Function Holes 409 | # 410 | ## str /1 -> 411 | ## io_lib:format("~p", [@]) |> lists:flatten(@) 412 | ## # = io_lib:format("~p", [@]) |> fun lists:flatten/1 413 | ## # = lists:flatten(fun (__1) -> io_lib:format("~p", [__1]) end) 414 | ## # str (_1) -> 415 | ## # lists:flatten(io_lib:format("~p", [_1])) 416 | ## # 417 | ## funs () -> 418 | ## Print = io:format("~p\n", [⋅]) 419 | ## # = fun (X) -> io:format("~p\n", [X]) end 420 | ## # = fun io:format("~p\n")/1 ◦ [⋅] 421 | ## # = fun io:format("~p\n")/1 ◦ fun (X) -> [X] end 422 | ## Id = ⋅ 423 | ## Incr = 1 + ⋅ 424 | ## Mul2 = ⋅ * ⋅ 425 | ## # Square ≠ ⋅ * ⋅ 426 | ## Order = ⋅ + ⋅ * ⋅ 427 | ## # = fun (X) -> fun (Y) -> fun (Z) -> 428 | ## Z + X * Y 429 | ## end end end 430 | # 431 | ## stream (S) -> 432 | ## Qqch = c(b(a(S))) 433 | ## Qqch = S |> fun a/1 434 | ## |> fun b/1 435 | ## |> fun c/1 436 | ## Qqch = S |> a(.) # ie no nesting? 437 | ## |> b(.) 438 | ## |> c(.) 439 | ## # |> io:format("~p\n", [.]) 440 | ## # |> fun lists:sum/1 441 | ## Sum = lists:foldl(fun erlang:'+'/2, 442 | ## 0, 443 | ## lists:map(fun erlang:length/1, ["wef", "yo", "coucou"])) 444 | ## Sum = ["wef", "yo", "coucou"] 445 | ## |> lists:map(fun erlang:length/1, ⋅) 446 | ## |> lists:foldl(⋅+⋅, 0, ⋅) # No issue: this is fun lists:foldl(…, …)/1 447 | ## Sum = ["wef", "yo", "coucou"] 448 | ## |> lists:foldl(fun (Word, Acc) -> length(Word) + Acc end, 0, ⋅) 449 | ## AutreTruc = S |> bite(⋅, ⋅+1) 450 | ## AutreTruc = S |> bite(º, º+1) 451 | ## # == S |> fun (X) -> bite(X, .+1) end 452 | ## Precedence? = ( ⋅*(⋅+⋅) )(1, 2, 3) 453 | ## # == 1*(2+3) or == 3*(1+2) ? 454 | ## # Is ⋅-notation even allowed outside |>-pipes ? 455 | 456 | test_range_matching (Thing) -> 457 | case Thing of [1..6] -> coucou end 458 | ## <=> case Thing of [1,2,3,4,5,6] -> ... 459 | 460 | #70 461 | test_disjoint_union_matching (Thing) -> 462 | case Thing of 1 | 2 | 3 -> hi end 463 | # <=> 464 | # case Thing of 465 | # 1 when Thing == 1; … end 466 | # 1. Allow usage of this **|** only inside `case`s or `cond`s! 467 | # 2. Cleverly warn on such usage: `… T | is_tuple(T) -> …` 468 | 469 | fcall_and_funs (F, B, N = 0) -> 470 | a:B(c) (d) ## fcall then atom `d` 471 | _FCall = (F()) ## Erlang: just `F()`, no extra braces 472 | _FunFCall = ((fun a/0) ()) ## `(fun a/0 ())` forbidden 473 | _Fun = fun a:B(c)/N 474 | A B (1+2) * (2+3) ## No fcall `B(1+2)` 475 | A (B (1+2)) * (2+3) ## fcall to `B(1+2)`, none to `A`. 476 | 477 | fmap_equivalent :: (#{session_context}) -> '':return(#{session_context}) 478 | fmap_equivalent (Context) -> 479 | case 480 | Context |> fun maybe_resource_exists/1 481 | -< fun maybe_validates/1 482 | -< fun maybe_load_something/1 483 | -< fun maybe_save_result/1 484 | of 485 | {ok, Context'} -> Context' 486 | {error, Reason}=_E -> 487 | lager:error("handling ~s failed: ~p" 488 | , [#{Context session_context request_id}, _E]) 489 | session:reply_failure(Reason, Context) 490 | end 491 | 492 | -------------------------------------------------------------------------------- /test/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/1.png -------------------------------------------------------------------------------- /test/1.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (atom ok)))))))) ) 2 | -------------------------------------------------------------------------------- /test/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/10.png -------------------------------------------------------------------------------- /test/10.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom p) (args ( (matchables (matchable (var Str))) )) (lra ->) (seqExprs (expr (matchable (var F)) = (expr (fun (fun_ fun) (exprMax (term (atom io))) : (exprMax (term (atom format))) (args ( (matchables (matchable (term (string "~p\n")))) )) / (integer 1)))) (expr (functionCall ( (exprMax (var F)) (params ( (exprs (expr (exprMax (var Str)))) )) ))))))) ) 2 | -------------------------------------------------------------------------------- /test/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/11.png -------------------------------------------------------------------------------- /test/11.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom b) (args ( )) (lra ->) (seqExprs (expr (begin begin (seqExprs (expr (functionCall (atom do) (params ( )))) (expr (functionCall (atom some) (params ( )))) (expr (exprMax (term (atom stuff))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/12.png -------------------------------------------------------------------------------- /test/12.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom 'p&to_list') (args ( (matchables (matchable (var Arg))) )) (lra ->) (seqExprs (expr (case_ case (expr (exprMax (var Arg))) (of of (clauses (clauseGuard (matchable (var T)) (guard when (expr (functionCall (atom is_tuple) (params ( (exprs (expr (exprMax (var T)))) ))))) (lra ->) (seqExprs (expr (lc [ (seqExprs (expr (functionCall (atom p) (params ( (exprs (expr (exprMax (var X)))) )))) (expr (exprMax (var X)))) (gens (gen_ | (gen (matchable (var X)) (generator <~) (expr (exprMax (var T)))))) ])))) (clauseGuard (matchable (var L)) (guard when (expr (functionCall (atom is_list) (params ( (exprs (expr (exprMax (var L)))) ))))) (lra ->) (seqExprs (expr (lc [ (seqExprs (expr (functionCall (atom p) (params ( (exprs (expr (exprMax (var X)))) )))) (expr (exprMax (var X)))) (gens (gen_ | (gen (matchable (var X)) (generator <-) (expr (exprMax (var L)))))) ])))) (clauseGuard (matchable (var B)) (guard when (expr (functionCall (atom is_binary) (params ( (exprs (expr (exprMax (var B)))) ))))) (lra ->) (seqExprs (expr (lc [ (seqExprs (expr (functionCall (atom p) (params ( (exprs (expr (exprMax (var X)))) )))) (expr (exprMax (var X)))) (gens (gen_ | (gen (matchable (var X)) (generator <=) (expr (exprMax (var B)))))) ])))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/13.png -------------------------------------------------------------------------------- /test/13.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom map) (args ( (matchables (matchable (var F)) , (matchable (var A))) )) (guard when (expr (functionCall (atom is_tuple) (params ( (exprs (expr (exprMax (var A)))) ))))) (lra ->) (seqExprs (expr (tc { (seqExprs (expr (functionCall ( (exprMax (var F)) (params ( (exprs (expr (exprMax (var X)))) )) )))) (gens (gen_ | (gen (matchable (var X)) (generator <~) (expr (exprMax (var A)))))) })))))) ) 2 | -------------------------------------------------------------------------------- /test/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/14.png -------------------------------------------------------------------------------- /test/14.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom try…catch) (args ( )) (lra ->) (seqExprs (expr (try_ try (seqExprs (expr (functionCall (atom bla) (params ( )))) (expr (exprMax (var Expression)))) (of of (clauses (clause (matchable (var SuccessfulPattern1)) (lra ->) (seqExprs (expr (exprMax (var Expression1))))) (clause (matchable (var SuccessfulPattern2)) (lra ->) (seqExprs (expr (exprMax (var Expression2))))))) catch (catchClauses (catchClause (exprMax (var TypeOfError)) : (clause (matchable (var ExceptionPattern1)) (lra ->) (seqExprs (expr (exprMax (var Expression3)))))) (catchClause (exprMax (var TypeOfError)) : (clause (matchable (var ExceptionPattern2)) (lra ->) (seqExprs (expr (exprMax (var Expression4))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/15.png -------------------------------------------------------------------------------- /test/15.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom π) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (float_ 3.1415926535)))))))) ) 2 | -------------------------------------------------------------------------------- /test/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/16.png -------------------------------------------------------------------------------- /test/16.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom brilliant!) (args ( )) (lra ->) (seqExprs (expr (bc (bil <<) (seqExprs (expr (case_ case (expr (exprMax (var X))) (of of (clauses (clauseGuard (matchable (var Z)) (guard when (expr (functionCall (atom m) (params ( (exprs (expr (exprMax (var Z)))) ))))) (lra ->) (seqExprs (expr (exprMax (term (integer 0)))))) (clause (matchable (var Y)) (lra ->) (seqExprs (expr (exprMax (term (integer 1)))))))) end))) (gens (gen_ | (gen (matchable (var X)) (generator <~) (expr (exprMax (var _)))))) (bir >>))))))) ) 2 | -------------------------------------------------------------------------------- /test/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/17.png -------------------------------------------------------------------------------- /test/17.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom i+am+impressed) (args ( )) (lra ->) (seqExprs (expr (functionCall (atom work) (params ( )))) (expr (try_ try (seqExprs (expr (functionCall (atom talk) (params ( )))) (expr (matchable (var _Knight)) = (expr (exprMax (term (string "None shall Pass!"))))) (expr (matchable (var _Doubles)) = (expr (lc [ (seqExprs (expr (expr (exprMax (term (integer 2)))) (mulOp *) (expr (exprMax (var N))))) (gens (gen_ | (gen (matchable (var N)) (generator <-) (expr (exprMax (term (lr [ (expr (exprMax (term (integer 1)))) .. (expr (exprMax (term (integer 100)))) ]))))))) ]))) (expr (functionCall (atom throw) (params ( (exprs (expr (exprMax (term (atom up))))) )))) (expr (matchable (var _WillReturnThis)) = (expr (exprMax (term (atom tequila)))))) catch (catchClauses (catchClause (exprMax (var Exception)) : (clause (matchable (var Reason)) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom caught)))) , (expr (exprMax (var Exception))) , (expr (exprMax (var Reason)))) })))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/18.png -------------------------------------------------------------------------------- /test/18.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom export) (fas (fa (atom a) / (integer 0)) (fa (atom b) / (integer 1)) (fa (atom c) / (integer 234))))) ) 2 | -------------------------------------------------------------------------------- /test/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/19.png -------------------------------------------------------------------------------- /test/19.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (tr { (expr (exprMax (term (integer 2)))) .. (expr (exprMax (term (integer 3)))) })))) (expr (exprMax (term (br (bil <<) (expr (unOp -) (expr (exprMax (term (integer 6))))) .. (expr (exprMax (term (integer 18)))) (bir >>))))) (expr (exprMax (term (lr [ (expr (exprMax (var Min))) .. (expr (expr (functionCall (atom g) (params ( )))) (addOp -) (expr (exprMax (term (integer 2))))) ])))))))) ) 2 | -------------------------------------------------------------------------------- /test/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/2.png -------------------------------------------------------------------------------- /test/2.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom len) (args ( (matchables (matchable (var L))) )) (guard when (expr (functionCall (atom is_list) (params ( (exprs (expr (exprMax (var L)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom length) (params ( (exprs (expr (exprMax (var L)))) )))))))) (block (def (func (atom len) (args ( (matchables (matchable (var T))) )) (guard when (expr (functionCall (atom is_tuple) (params ( (exprs (expr (exprMax (var T)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom size) (params ( (exprs (expr (exprMax (var T)))) )))))))) ) 2 | -------------------------------------------------------------------------------- /test/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/20.png -------------------------------------------------------------------------------- /test/20.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (list [ (expr (exprMax (term (integer 1)))) (tail | (expr (exprMax (term (list [ (expr (exprMax (term (integer 2)))) (tail | (expr (exprMax (term (list [ (expr (exprMax (term (integer 3)))) (tail | (expr (exprMax (term (list [ ])))) ]))))) ]))))) ]))))))))) ) 2 | -------------------------------------------------------------------------------- /test/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/21.png -------------------------------------------------------------------------------- /test/21.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (var Thu))) )) (lra ->) (seqExprs (expr (exprMax (term (atom gaim)))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/22.png -------------------------------------------------------------------------------- /test/22.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom many_args) (args ( (matchables (matchable (var A)) , (matchable (term (list [ (expr (exprMax (var H))) (tail | (expr (exprMax (var T))) ])))) , (matchable (term (string "str"))) , (matchable (unOp -) (matchable (term (integer 2))))) )) (lra ->) (seqExprs (expr (exprMax (term (atom ok)))))))) ) 2 | -------------------------------------------------------------------------------- /test/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/23.png -------------------------------------------------------------------------------- /test/23.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom fun_clauses) (args ( )) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (term (integer 123)))) )) (lra ->) (seqExprs (expr (exprMax (term (string "123")))))) (funClause (args ( (matchables (matchable (term (integer 231)))) )) (lra ->) (seqExprs (expr (exprMax (term (atom 'deux-trois-un')))))) (funClause (args ( (matchables (matchable (term (atom stop)))) )) (lra ->) (seqExprs (expr (functionCall (atom bla) (params ( (exprs (expr (exprMax (term (integer 1)))) , (expr (exprMax (term (integer 2)))) , (expr (exprMax (var B))) , (expr (exprMax (term (list [ (expr (exprMax (term (char_ $*)))) (tail ])))))) )))) (expr (functionCall (atom babebibobu) (params ( )))) (expr (case_ case (expr (functionCall (atom do) (params ( )))) (of of (clauses (clause (matchable (term (atom hammer))) (lra ->) (seqExprs (expr (functionCall (atom lel) (params ( )))) (expr (exprMax (term (atom so_funny)))))) (clause (matchable (var _)) (lra ->) (seqExprs (expr (exprMax (term (atom something)))))))) end)))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/24.png -------------------------------------------------------------------------------- /test/24.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (case_ case (expr (exprMax (term (atom asd)))) (of of (clauses (clause (matchable (term (atom a))) (lra ->) (seqExprs (expr (exprMax (term (atom a)))))) (clause (matchable (term (atom s))) (lra ->) (seqExprs (expr (exprMax (term (atom smth)))))) (clause (matchable ( (matchable (term (atom d))) )) (lra ->) (seqExprs (expr (exprMax (term (atom ah+ah!)))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/25.png -------------------------------------------------------------------------------- /test/25.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom len) (dim ::) (tyFun ( (tyMaxs (tyMax (type (subtype (atom list) ( ))))) ) (lra ->) (tyMax (type (subtype (atom pos_integer) ( ))) | (type (integer 0))))) (func (atom len) (args ( (matchables (matchable (var L))) )) (guard when (expr (functionCall (atom is_list) (params ( (exprs (expr (exprMax (var L)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom length) (params ( (exprs (expr (exprMax (var L)))) )))))))) ) 2 | -------------------------------------------------------------------------------- /test/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/26.png -------------------------------------------------------------------------------- /test/26.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom len) (dim ::) (tyFun ( (tyMaxs (tyMax (type (subtype (atom list) ( ))))) ) (lra ->) (tyMax (type (subtype (atom pos_integer) ( )))))) (func (atom len) (args ( (matchables (matchable (var L))) )) (lra ->) (seqExprs (expr (functionCall (atom length) (params ( (exprs (expr (exprMax (var L)))) )))))))) (block (def (func (atom len) (args ( (matchables (matchable (var T))) )) (guard when (expr (functionCall (atom is_tuple) (params ( (exprs (expr (exprMax (var T)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom size) (params ( (exprs (expr (exprMax (var T)))) )))))))) ) 2 | -------------------------------------------------------------------------------- /test/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/27.png -------------------------------------------------------------------------------- /test/27.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom '!') (args ( (matchables (matchable (term (integer 0)))) )) (lra ->) (seqExprs (expr (exprMax (term (integer 1)))))))) (block (def (spec (atom '!') (dim ::) (tyFun ( (tyMaxs (tyMax (type (var N)))) ) (lra ->) (tyMax (type (var M)))) when (tyGuards (tyGuard (var N) (dim ::) (tyMax (type (subtype (atom pos_integer) ( ))))) (tyGuard (var M) (dim ::) (tyMax (type (subtype (atom pos_integer) ( ))))))) (func (atom '!') (args ( (matchables (matchable (var N))) )) (lra ->) (seqExprs (expr (expr (exprMax (var N))) (mulOp *) (expr (functionCall : (params ( (exprs (expr (expr (exprMax (var N))) (addOp -) (expr (exprMax (term (integer 1)))))) ))))))))) ) 2 | -------------------------------------------------------------------------------- /test/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/28.png -------------------------------------------------------------------------------- /test/28.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom '!') (dim ::) (tyFun ( ) (lra ->) (tyMax (type (fun_ fun) ( (tyFun ( (tyMaxs (tyMax (type (var N)))) ) (lra ->) (tyMax (type (var N)))) )))) when (tyGuards (tyGuard (var N) (dim ::) (tyMax (type (subtype (atom pos_integer) ( ))))))) (func (atom '!') (args ( )) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (term (integer 0)))) )) (lra ->) (seqExprs (expr (exprMax (term (integer 1)))))) (funClause (args ( (matchables (matchable (var N))) )) (guard when (expr (expr (exprMax (var N))) (compOp >) (expr (exprMax (term (integer 0)))))) (lra ->) (seqExprs (expr (expr (exprMax (var N))) (mulOp *) (expr (functionCall : (params ( (exprs (expr (expr (exprMax (var N))) (addOp -) (expr (exprMax (term (integer 1)))))) ))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/29.png -------------------------------------------------------------------------------- /test/29.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom flat.map) (args ( (matchables (matchable (var Fun)) , (matchable (var List))) )) (lra ->) (seqExprs (expr (functionCall ( (exprMax ( (expr (expr (fun (fun_ fun) (exprMax (term (atom append))) / (integer 1))) (composeOp .) (expr (fun (fun_ fun) (exprMax (term (atom map))) / (integer 2)))) )) (params ( (exprs (expr (exprMax (var Fun))) , (expr (exprMax (var List)))) )) ))))))) ) 2 | -------------------------------------------------------------------------------- /test/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/3.png -------------------------------------------------------------------------------- /test/3.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (functionCall (atom thiS) (params ( )))) (expr (matchable (var W)) = (expr (exprMax (term (atom ill))))) (expr (functionCall (exprMax (term (atom tot))) : (exprMax (term (atom ally))) (params ( (exprs (expr (expr (exprMax (var W))) (addOp +) (expr (exprMax (term (atom ork)))))) )))))))) (block (def (func (atom g) (args ( )) (lra ->) (seqExprs (expr (functionCall (atom i_ll) (params ( (exprs (expr (exprMax (term (atom give))))) )))) (expr (functionCall (exprMax (term (atom my))) : (exprMax (term (atom hand))) (params ( )))) (expr (exprMax (term (atom to_cut)))))))) ) 2 | -------------------------------------------------------------------------------- /test/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/30.png -------------------------------------------------------------------------------- /test/30.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (fun_func (fa (atom flatmap) / (integer 2)) (lra ->) (seqExprs (expr (expr (fun (fun_ fun) (exprMax (term (atom append))) / (integer 1))) (composeOp .) (expr (fun (fun_ fun) (exprMax (term (atom map))) / (integer 2)))))))) ) 2 | -------------------------------------------------------------------------------- /test/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/31.png -------------------------------------------------------------------------------- /test/31.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (fun_func (fa (atom f) / (integer 1)) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (term (integer 0)))) )) (lra ->) (seqExprs (expr (exprMax (term (integer 1)))))) (funClause (args ( (matchables (matchable (var N))) )) (lra ->) (seqExprs (expr (expr (exprMax (var N))) (mulOp *) (expr (functionCall : (params ( (exprs (expr (expr (exprMax (var N))) (addOp -) (expr (exprMax (term (integer 1)))))) ))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/32.png -------------------------------------------------------------------------------- /test/32.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (list [ (expr (functionCall (exprMax ( (expr (functionCall (atom a) (params ( )))) )) : (exprMax (term (atom loop))) (params ( )))) (tail ]))))))))) ) 2 | -------------------------------------------------------------------------------- /test/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/33.png -------------------------------------------------------------------------------- /test/33.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (matchable (matchable (matchable (var A)) = (matchable (var B))) = (matchable (var C))) = (expr (exprMax (term (atom d))))))))) ) 2 | -------------------------------------------------------------------------------- /test/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/34.png -------------------------------------------------------------------------------- /test/34.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom zip) (dim ::) (tyFun ( (tyMaxs (tyMax (type [ (tyMax (type (subtype (atom term) ( )))) ])) , (tyMax (type [ (tyMax (type (subtype (atom term) ( )))) ]))) ) (lra ->) (tyMax (type [ (tyMax (type { (tyMaxs (tyMax (type (subtype (atom term) ( )))) , (tyMax (type (subtype (atom term) ( ))))) })) ])))) (func (atom zip) (args ( (matchables (matchable (term (list [ (expr (exprMax (var X))) (tail | (expr (exprMax (var Xs))) ])))) , (matchable (term (list [ (expr (exprMax (var Y))) (tail | (expr (exprMax (var Ys))) ]))))) )) (lra ->) (seqExprs (expr (lc [ (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (var X'))) , (expr (exprMax (var Y')))) }))))) (gens (gen_ | (gen (matchable (var X')) (generator <-) (expr (exprMax (term (list [ (expr (exprMax (var X))) (tail | (expr (exprMax (var Xs))) ]))))))) (gen_ | (gen (matchable (var Y')) (generator <-) (expr (exprMax (term (list [ (expr (exprMax (var Y))) (tail | (expr (exprMax (var Ys))) ])))))))) ])))))) (block (def (func (atom zip) (args ( (matchables (matchable (term (list [ ]))) , (matchable (term (list [ ])))) )) (lra ->) (seqExprs (expr (exprMax (term (list [ ])))))))) ) 2 | -------------------------------------------------------------------------------- /test/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/35.png -------------------------------------------------------------------------------- /test/35.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom fizz_buzz) (args ( (matchables (matchable (var N))) )) (lra ->) (seqExprs (expr (lc [ (seqExprs (expr (case_ case (expr (exprMax (term (tuple { (exprs (expr (expr (exprMax (var X))) (mulOp rem) (expr (exprMax (term (integer 3))))) , (expr (expr (exprMax (var X))) (mulOp rem) (expr (exprMax (term (integer 5)))))) })))) (of of (clauses (clause (matchable (term (tuple { (exprs (expr (exprMax (term (integer 0)))) , (expr (exprMax (term (integer 0))))) }))) (lra ->) (seqExprs (expr (exprMax (term (atom fizzbuzz)))))) (clause (matchable (term (tuple { (exprs (expr (exprMax (term (integer 0)))) , (expr (exprMax (var _)))) }))) (lra ->) (seqExprs (expr (exprMax (term (atom fizz)))))) (clause (matchable (term (tuple { (exprs (expr (exprMax (var _))) , (expr (exprMax (term (integer 0))))) }))) (lra ->) (seqExprs (expr (exprMax (term (atom buzz)))))) (clause (matchable (term (tuple { (exprs (expr (exprMax (var _))) , (expr (exprMax (var _)))) }))) (lra ->) (seqExprs (expr (exprMax (var X))))))) end))) (gens (gen_ | (gen (matchable (var X)) (generator <-) (expr (exprMax (term (lr [ (expr (exprMax (term (integer 1)))) .. (expr (exprMax (var N))) ]))))))) ])))))) ) 2 | -------------------------------------------------------------------------------- /test/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/36.png -------------------------------------------------------------------------------- /test/36.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (expr (functionCall (exprMax (term (atom string))) : (exprMax (term (atom sub_string))) (params ( (exprs (expr (exprMax (var OptStr))) , (expr (exprMax (var P))) , (expr (expr (exprMax (var P))) (addOp +) (expr (exprMax (term (integer 2)))))) )))) (compOp >=) (expr (exprMax (term (string "3.4"))))))))) ) 2 | -------------------------------------------------------------------------------- /test/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/37.png -------------------------------------------------------------------------------- /test/37.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom export) (fas (fa (atom start) / (integer 1)) , (integer 2)))) ) 2 | -------------------------------------------------------------------------------- /test/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/38.png -------------------------------------------------------------------------------- /test/38.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom verify_int) (args ( (matchables (matchable (var Int)) , (matchable (var Max))) )) (lra ->) (seqExprs (expr (expr (exprMax (term (atom false)))) (orelse ||) (expr (expr (expr (functionCall (atom verify_strict_int) (params ( (exprs (expr (exprMax (var Int)))) )))) (andalso &&) (expr (functionCall (atom verify_strict_int) (params ( (exprs (expr (exprMax (var Max)))) ))))) (andalso &&) (expr (expr (exprMax (var Int))) (compOp =<) (expr (exprMax (var Max)))))))))) ) 2 | -------------------------------------------------------------------------------- /test/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/39.png -------------------------------------------------------------------------------- /test/39.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (fun_func (fa (atom factorial) / (integer 1)) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (term (atom f)))) )) (lra ->) (seqExprs (expr (fun (fun_ fun) : / (integer 1))))) (funClause (args ( (matchables (matchable (term (atom ff)))) )) (lra ->) (seqExprs (expr (fun (fun_ fun) : (args ( )) / (integer 1))))) (funClause (args ( (matchables (matchable (term (integer 0)))) )) (lra ->) (seqExprs (expr (exprMax (term (integer 1)))))) (funClause (args ( (matchables (matchable (var N))) )) (guard when (expr (expr (exprMax (var N))) (compOp >) (expr (exprMax (term (integer 0)))))) (lra ->) (seqExprs (expr (expr (exprMax (var N))) (mulOp *) (expr (functionCall : (params ( (exprs (expr (expr (exprMax (var N))) (addOp -) (expr (exprMax (term (integer 1)))))) ))))))) (funClause (args ( (matchables (matchable (term (atom f24)))) )) (lra ->) (seqExprs (expr (fun (fun_ fun) : (args ( (matchables (matchable (term (integer 4)))) )) / (integer 0))))) (funClause (args ( (matchables (matchable (var ___))) )) (lra ->) (seqExprs (expr (functionCall : (params ( (exprs (expr (exprMax (term (integer 4))))) )))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/4.png -------------------------------------------------------------------------------- /test/4.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom length) (args ( (matchables (matchable (term (list [ ])))) )) (lra ->) (seqExprs (expr (exprMax (term (integer 0)))))))) (block (def (func (atom length) (args ( (matchables (matchable (term (list [ (expr (exprMax (var _))) (tail | (expr (exprMax (var T))) ]))))) )) (lra ->) (seqExprs (expr (expr (exprMax (term (integer 1)))) (addOp +) (expr (functionCall : (params ( (exprs (expr (exprMax (var T)))) ))))))))) ) 2 | -------------------------------------------------------------------------------- /test/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/40.png -------------------------------------------------------------------------------- /test/40.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (fun (fun_ fun) (exprMax (term (atom io))) : (exprMax (term (atom format))) (args ( (matchables (matchable (term (string "~p\n")))) )) / (integer 1))) (expr (expr (exprMax ( (expr (exprMax (term (list [ (expr (exprMax (term (atom dg)))) (tail ]))))) ))) (mulOp /) (expr (exprMax (term (integer 1))))))))) (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (expr (functionCall ( (exprMax ( (expr (fun (fun_ fun) (exprMax (term (atom io))) : (exprMax (term (atom format))) (args ( (matchables (matchable (term (string "~p\n")))) )) / (integer 1))) )) (params ( (exprs (expr (exprMax (term (list [ (expr (exprMax (term (atom dg)))) (tail ])))))) )) ))) (mulOp /) (expr (exprMax (term (integer 1))))))))) ) 2 | -------------------------------------------------------------------------------- /test/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/41.png -------------------------------------------------------------------------------- /test/41.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (fun (fun_ fun) (exprMax ( (expr (fun (fun_ fun) (exprMax (term (atom io))) : (exprMax (term (atom format))) (args ( (matchables (matchable (term (string "~p\n")))) )) / (integer 1))) )) (args ( (matchables (matchable (term (list [ (expr (exprMax (term (atom dg)))) (tail ]))))) )) / (integer 0))))))) ) 2 | -------------------------------------------------------------------------------- /test/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/42.png -------------------------------------------------------------------------------- /test/42.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom import) (fas (fa (atom map) / (integer 2)) (fa (atom foreach) / (integer 2))) of (term (atom lists)))) ) 2 | -------------------------------------------------------------------------------- /test/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/43.png -------------------------------------------------------------------------------- /test/43.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (matchable (var A)) = (expr (exprMax (term (integer 10))))) (expr (matchable (var B)) = (expr (unOp -) (expr (exprMax (term (integer 234)))))) (expr (matchable (var C)) = (expr (exprMax (term (integer 16#AB10F))))) (expr (matchable (var D)) = (expr (exprMax (term (integer 2#110111010))))) (expr (matchable (var E)) = (expr (exprMax (term (char_ $A))))) (expr (matchable (var F)) = (expr (exprMax (term (float_ 17.368))))) (expr (matchable (var G)) = (expr (unOp -) (expr (exprMax (term (float_ 56.654)))))) (expr (matchable (var H)) = (expr (exprMax (term (float_ 12.34E-10))))))))) ) 2 | -------------------------------------------------------------------------------- /test/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/44.png -------------------------------------------------------------------------------- /test/44.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (matchable (term (atom ok))) = (expr (functionCall (exprMax (term (atom a))) : (exprMax (term (atom b))) (params ( ))))))))) ) 2 | -------------------------------------------------------------------------------- /test/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/45.png -------------------------------------------------------------------------------- /test/45.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom import) (term (atom master)) of (term (string "github.com/anon42/app1")))) (block (attribute (atom import) (term (atom v1.2)) of (term (string "github.com/anon42/app1")))) (block (attribute (atom import) (term (atom master)) of (term (string "bitbucket.org/user2/repo2")))) (block (attribute (atom export) (fas (fa (atom main) / (integer 0))))) (block (def (func (atom main) (args ( )) (lra ->) (seqExprs (expr (matchable (term (atom ok))) = (expr (functionCall (exprMax (term (atom app2))) : (exprMax (term (atom bla))) (params ( (exprs (expr (exprMax (term (atom bla)))) , (expr (exprMax (term (atom bla))))) ))))) (expr (functionCall (exprMax (term (atom app1))) : (exprMax (term (atom master))) : (exprMax (term (atom app1))) : (exprMax (term (atom f))) (params ( )))) (expr (functionCall (exprMax (term (atom app1))) : (exprMax (term (atom v1.2))) : (exprMax (term (atom app1))) : (exprMax (term (atom f))) (params ( )))))))) ) 2 | -------------------------------------------------------------------------------- /test/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/46.png -------------------------------------------------------------------------------- /test/46.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( (matchables (matchable (term (atom '$char')))) )) (lra ->) (seqExprs (expr (exprMax (term (char_ $c)))))))) ) 2 | -------------------------------------------------------------------------------- /test/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/47.png -------------------------------------------------------------------------------- /test/47.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (integer 1_000)))) (expr (exprMax (term (integer 1_23_777)))) (expr (exprMax (term (integer 1_00#9_999_957)))) (expr (exprMax (term (float_ 10.000_001E-2_345)))))))) ) 2 | -------------------------------------------------------------------------------- /test/48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/48.png -------------------------------------------------------------------------------- /test/48.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom f) (dim ::) (tyFun ( ) (lra ->) (tyMax (type (tyBinary (bil <<) (tyBinaryBase (var _) : (type (var Integer))) , (tyBinaryUnit (var _) : (var _) * (type (var OtherInteger))) (bir >>)))))) (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (binary (bil <<) (binElements (binElement (expr (exprMax (term (integer 42))))) , (binElement (expr (exprMax (term (integer 3)))) : (exprMax (term (integer 2)))) , (binElement (expr (functionCall (atom g) (params ( )))) : (exprMax ( (expr (functionCall (atom h) (params ( )))) ))) , (binElement (expr (expr (exprMax (term (integer 1)))) (addOp +) (expr (exprMax (term (integer 2))))) : (exprMax (term (integer 3))) / (binType (atom ba)) - (binType (atom be)) - (binType (atom bi)))) (bir >>))))))))) ) 2 | -------------------------------------------------------------------------------- /test/49.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/49.png -------------------------------------------------------------------------------- /test/49.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom my_other_function) (dim ::) (tyFun ( (tyMaxs (tyMax (type (subtype (atom names) ( )))) , (tyMax (type (subtype (atom places) ( ))))) ) (lra →) (tyMax (type (subtype (atom atom) ( )))))) (func (atom my_other_function) (args ( (matchables (matchable (var Names)) , (matchable (var Places))) )) (lra ->) (seqExprs (expr (matchable (var R1)) = (expr (functionCall (atom ø) (params ( (exprs (expr (exprMax (var Ø))) , (expr (exprMax (var Ça))) , (expr (exprMax (var Names)))) ))))) (expr (matchable (var R2)) = (expr (functionCall (atom µ) (params ( (exprs (expr (exprMax (var Ð))) , (expr (exprMax (var Æ))) , (expr (exprMax (var Places)))) ))))) (expr (if_ if (expr (expr (exprMax (var R1))) (compOp ≥) (expr (exprMax (var R2)))) (lra ->) (expr (exprMax (term (atom ok)))) if not (lra ->) (expr (exprMax (term (atom ko)))))))))) ) 2 | -------------------------------------------------------------------------------- /test/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/5.png -------------------------------------------------------------------------------- /test/5.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (case_ case (expr (functionCall (exprMax (term (atom orddict))) : (exprMax (term (atom find))) (params ( (exprs (expr (exprMax (var Node))) , (expr (exprMax (var Nodes)))) )))) (of of (clauses (clause (matchable (term (tuple { (exprs (expr (exprMax (term (atom ok)))) , (expr (exprMax (var Ref)))) }))) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom down)))) , (expr (exprMax (term (atom node)))) , (expr (exprMax (var Ref)))) })))))) (clauseGuard (matchable (term (tuple { (exprs (expr (exprMax (term (atom ok)))) , (expr (exprMax (var WrongRef)))) }))) (guard when (expr (functionCall (atom is_reference) (params ( (exprs (expr (exprMax (var WrongRef)))) ))))) (lra ->) (seqExprs (expr (exprMax (term (atom 'not the one')))))) (clause (matchable (term (atom error))) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom ignore)))) , (expr (exprMax (term (atom not_monitored)))) , (expr (exprMax (var FD)))) })))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/50.png -------------------------------------------------------------------------------- /test/50.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (list [ (expr (lc [ (seqExprs (expr (functionCall (atom log) (params ( (exprs (expr (exprMax (var E)))) ))))) (gens (gen_ | (gen (matchable (var E)) (generator <-) (expr (exprMax (var LogVars)))))) ])) (tail | (expr (exprMax (var DebugOn))) ]))))))))) ) 2 | -------------------------------------------------------------------------------- /test/51.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/51.png -------------------------------------------------------------------------------- /test/51.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( (matchables (matchable (matchable (term (tuple { (exprs (expr (exprMax (term (atom connect)))) , (expr (exprMax (var _))) , (expr (exprMax (var To))) , (expr (exprMax (var _))) , (expr (exprMax (var _)))) }))) = (matchable (var Signal))) , (matchable (var To)) , (matchable (matchable (matchable (matchable (term (integer 1))) (addOp +) (matchable (term (integer 2)))) (addOp bxor) (matchable (term (integer 3)))) = (matchable (term (integer 0))))) )) (lra ->) (seqExprs (expr (matchable (var S)) = (expr (exprMax (term (string "prefixed string"))))) (expr (matchable (matchable (term (string "prefix"))) (listOp ++) (matchable (var Str))) = (expr (exprMax (var S)))) (expr (matchable (term (list [ (expr (exprMax (term (char_ $p)))) (tail , (expr (exprMax (term (char_ $r)))) (tail , (expr (exprMax (term (char_ $e)))) (tail , (expr (exprMax (term (char_ $f)))) (tail , (expr (exprMax (term (char_ $i)))) (tail , (expr (exprMax (term (char_ $x)))) (tail | (expr (exprMax (var _))) ]))))))))) = (expr (exprMax (var S)))))))) ) 2 | -------------------------------------------------------------------------------- /test/52.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/52.png -------------------------------------------------------------------------------- /test/52.tree: -------------------------------------------------------------------------------- 1 | (root (block (defty (atom b) ( ) (dim ::) (tyMax (type (atom true)) | (type (atom false))))) (block (def (spec (atom h) (dim ::) (tyFun ( (tyMaxs (tyMax (type (subtype (atom b) ( )))) , (tyMax (type (subtype (atom b) ( ))))) ) (lra ->) (tyMax (type (subtype (atom b) ( )))))) (func (atom h) (args ( (matchables (matchable (var A)) , (matchable (var B))) )) (lra ->) (seqExprs (expr (expr (exprMax (var A))) (mulOp band) (expr (exprMax (var B)))))))) ) 2 | -------------------------------------------------------------------------------- /test/53.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/53.png -------------------------------------------------------------------------------- /test/53.tree: -------------------------------------------------------------------------------- 1 | (root (block (defty (atom red_panda_food) ( ) (dim ::) (tyMax (type (atom bamboo)) | (type (atom birds)) | (type (atom eggs)) | (type (atom berries))))) (block (defty (atom squid_food) ( ) (dim ::) (tyMax (type (atom sperm_whale))))) (block (defty (atom feed) ( (tyMaxs (tyMax (type (var A)))) ) (dim ::) (tyMax (type (fun_ fun) ( (tyFun ( ) (lra ->) (tyMax (type (var A)))) ))))) (block (def (spec (atom feeder) (dim ::) (tyFun ( (tyMaxs (tyMax (type (atom red_panda)))) ) (lra ->) (tyMax (type (subtype (atom feed) ( (tyMaxs (tyMax (type (subtype (atom red_panda_food) ( ))))) )))))) (func (atom feeder) (args ( (matchables (matchable (term (atom red_panda)))) )) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( )) (lra ->) (seqExprs (expr (exprMax (term (atom bamboo)))))) end)))))) (block (def (spec (atom feeder) (dim ::) (tyFun ( (tyMaxs (tyMax (type (atom squid)))) ) (lra ->) (tyMax (type (subtype (atom feed) ( (tyMaxs (tyMax (type (subtype (atom squid_food) ( ))))) )))))) (func (atom feeder) (args ( (matchables (matchable (term (atom squid)))) )) (lra ->) (seqExprs (expr (fun (fun_ fun) (funClause (args ( )) (lra ->) (seqExprs (expr (exprMax (term (atom sperm_whale)))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/54.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/54.png -------------------------------------------------------------------------------- /test/54.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom 'key-value-stores') (args ( )) (lra ->) (seqExprs (expr (matchable (var M')) = (expr (exprMax (term (map (mapEmpty #{ })))))) (expr (matchable (var R')) = (expr (exprMax (record (recEmpty #{ (atom r) }))))) (expr (matchable (var M0)) = (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom k)))) => (expr (exprMax (term (atom v)))))) })))))) (expr (matchable (var R0)) = (expr (exprMax (record (recCreateMatch #{ (atom r) (recAssocs (recAssoc (atom k) = (expr (exprMax (term (atom v)))))) }))))) (expr (matchable (var M1)) = (expr (exprMax (term (map #{ (expr (exprMax (var M0))) (mapAssocs (mapAssoc (expr (exprMax (term (atom k)))) := (expr (exprMax (term (atom v2)))))) }))))) (expr (matchable (var R1)) = (expr (exprMax (record #{ (expr (exprMax (var R0))) (atom r) (recAssocs (recAssoc (atom k) = (expr (exprMax (term (atom v2)))))) })))) (expr (matchable (var V)) = (expr (exprMax (term (map (mapRead #{ (expr (exprMax (var M1))) (exprMax (term (atom k))) })))))) (expr (matchable (var V)) = (expr (exprMax (record (recRead #{ (expr (exprMax (var R1))) (atom r) (atom k) }))))) (expr (matchable (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom k)))) := (expr (exprMax (var V))))) })))) = (expr (exprMax (var M1)))) (expr (matchable (record (recCreateMatch #{ (atom r) (recAssocs (recAssoc (atom k) = (expr (exprMax (var V))))) }))) = (expr (exprMax (var R1)))))))) ) 2 | -------------------------------------------------------------------------------- /test/55.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/55.png -------------------------------------------------------------------------------- /test/55.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom nesting) (args ( )) (lra ->) (seqExprs (expr (matchable (var R)) = (expr (exprMax (record (recCreateMatch #{ (atom person) (recAssocs (recAssoc (atom name) = (expr (exprMax (record (recCreateMatch #{ (atom name) (recAssocs (recAssoc (atom first) = (expr (exprMax (term (string "Robert"))))) , (recAssoc (atom last) = (expr (exprMax (term (string "Virding")))))) }))))) , (recAssoc (atom phone) = (expr (expr (exprMax (term (integer 120)))) (addOp +) (expr (exprMax (term (integer 3))))))) }))))) (expr (matchable (var First)) = (expr (exprMax (record (recRead #{ (expr (exprMax (record (recRead #{ (expr (exprMax (var R))) (atom person) (atom name) })))) (atom name) (atom first) }))))) (expr (matchable (var M)) = (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom name)))) => (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom first)))) => (expr (exprMax (term (string "Robert"))))) , (mapAssoc (expr (exprMax (term (atom last)))) => (expr (exprMax (term (string "Virding")))))) })))))) , (mapAssoc (expr (exprMax (term (atom phone)))) => (expr (expr (exprMax (term (integer 120)))) (addOp +) (expr (exprMax (term (integer 3))))))) })))))) (expr (matchable (var First)) = (expr (exprMax (term (map (mapRead #{ (expr (exprMax (term (map (mapRead #{ (expr (exprMax (var M))) (exprMax (term (atom name))) }))))) (exprMax (term (atom first))) })))))))))) ) 2 | -------------------------------------------------------------------------------- /test/56.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/56.png -------------------------------------------------------------------------------- /test/56.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom a))))) })))) (expr (exprMax (term (tuple { (exprs (expr (matchable (term (atom a))) = (expr (exprMax (term (atom b)))))) })))) (expr (exprMax (term (tuple { (exprs (expr (matchable (term (atom a))) = (expr (exprMax (term (atom b))))) , (expr (matchable (term (atom c))) = (expr (exprMax (term (atom d)))))) })))) (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom b)))) , (expr (matchable (term (atom c))) = (expr (exprMax (term (atom d)))))) })))))))) ) 2 | -------------------------------------------------------------------------------- /test/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/57.png -------------------------------------------------------------------------------- /test/57.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom some_more) (args ( )) (lra ->) (seqExprs (expr (matchable (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (string "favorite")))) := (expr (exprMax (var Animal)))) , (mapAssoc (expr (exprMax (var Animal))) := (expr (exprMax (var Name))))) })))) = (expr (exprMax (term (map #{ (expr (exprMax (var Pets))) (mapAssocs (mapAssoc (expr (exprMax (term (string "favorite")))) := (expr (exprMax (term (string "dog")))))) }))))) (expr (matchable (var FoggyPlaces)) = (expr (lc [ (seqExprs (expr (exprMax (var X)))) (gens (gen_ | (gen (matchable (var X)) := (matchable (term (atom fog))) <- (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom montreal)))) => (expr (exprMax (term (atom storms))))) , (mapAssoc (expr (exprMax (term (atom london)))) => (expr (exprMax (term (atom fog))))) , (mapAssoc (expr (exprMax (term (atom paris)))) => (expr (exprMax (term (atom sun)))))) })))))))) ]))) (expr (matchable (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom boston)))) := (expr (exprMax (term (atom foggy))))) , (mapAssoc (expr (exprMax (term (atom london)))) := (expr (exprMax (term (atom foggy)))))) })))) = (expr (mc #{ (expr (exprMax (var X))) => (expr (exprMax (term (atom foggy)))) (gens (gen_ | (gen (matchable (var X)) (generator <-) (expr (exprMax (term (list [ (expr (exprMax (term (atom london)))) (tail , (expr (exprMax (term (atom boston)))) (tail ]))))))))) }))) (expr (matchable (var NPants)) = (expr (exprMax (record (recCreateMatch #{ (atom pants) (recAssocs (recAssoc (var _) = (expr (exprMax (term (atom undef)))))) }))))))))) ) 2 | -------------------------------------------------------------------------------- /test/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/58.png -------------------------------------------------------------------------------- /test/58.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom note) (args ( )) (lra ->) (seqExprs (expr (matchable (var M)) = (expr (exprMax (term (map #{ (expr (exprMax (var A))) (mapAssocs (mapAssoc (expr (exprMax (var B))) => (expr (exprMax (term (atom true)))))) }))))) (expr (matchable (var M')) = (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (expr (exprMax (var A))) (compOp >) (expr (exprMax (var B)))) => (expr (exprMax (term (atom true)))))) })))))) (expr (matchable (var M'')) = (expr (exprMax (term (map #{ (expr (functionCall (atom g) (params ( )))) (mapAssocs (mapAssoc (expr (expr (exprMax (var A))) (compOp >) (expr (exprMax (var B)))) => (expr (functionCall (atom yo) (params ( )))))) }))))))))) ) 2 | -------------------------------------------------------------------------------- /test/59.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/59.png -------------------------------------------------------------------------------- /test/59.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom record) (term (atom name)) of (tyRecordFields { (tyRecordField (atom first) (dim ::) (type (subtype (atom string) ( )))) , (tyRecordField (atom last) (dim ::) (type (subtype (atom string) ( )))) }))) (block (defty (atom name) ( ) (dim ::) (tyMax (type (tyRecord #{ (atom name) }))))) (block (attribute (atom record) (term (atom person)) of (tyRecordFields { (tyRecordField (atom name) (dim ::) (type (subtype (atom name) ( )))) , (tyRecordField (atom phone) (dim ::) (type (subtype (atom pos_integer) ( )))) }))) (block (def (func (atom record_def_and_usage) (args ( )) (lra ->) (seqExprs (expr (matchable (var R')) = (expr (exprMax (record (recCreateMatch #{ (atom name) (recAssocs (recAssoc (atom first) = (expr (exprMax (term (string "Robert"))))) , (recAssoc (atom last) = (expr (exprMax (term (string "Virding")))))) }))))) (expr (matchable (var R)) = (expr (exprMax (record (recCreateMatch #{ (atom person) (recAssocs (recAssoc (atom name) = (expr (exprMax (var R')))) , (recAssoc (atom phone) = (expr (exprMax (term (integer 123)))))) }))))) (expr (matchable (var First)) = (expr (exprMax (record (recRead #{ (expr (exprMax (record (recRead #{ (expr (exprMax (var R))) (atom person) (atom name) })))) (atom name) (atom first) }))))))))) ) 2 | -------------------------------------------------------------------------------- /test/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/6.png -------------------------------------------------------------------------------- /test/6.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom 'f') (args ( )) (lra ->) (seqExprs (expr (receive receive (clauses (clauseGuard (matchable (term (tuple { (exprs (expr (exprMax (term (atom 'ok')))) , (expr (exprMax (var WrongRef)))) }))) (guard when (expr (functionCall (atom 'is_reference') (params ( (exprs (expr (exprMax (var WrongRef)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom 'smth') (params ( )))) (expr (exprMax (term (atom 'not the one')))))) (clause (matchable (term (atom 'error'))) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom 'ignore')))) , (expr (exprMax (term (atom 'not_monitored')))) , (expr (exprMax (var FD)))) }))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/60.png -------------------------------------------------------------------------------- /test/60.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (matchable (var R)) = (expr (functionCall (exprMax (term (atom a))) : (exprMax (term (atom g))) (params ( ))))) (expr (matchable (var R)) = (expr (functionCall (exprMax ( (expr (exprMax (term (atom a)))) )) : (exprMax (term (atom g))) (params ( ))))) (expr (matchable (var R)) = (expr (functionCall (exprMax (term (atom a))) : (exprMax (term (atom g))) (params ( ))))))))) ) 2 | -------------------------------------------------------------------------------- /test/61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/61.png -------------------------------------------------------------------------------- /test/61.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (string "bla\nbla\"bla")))) , (expr (exprMax (term (string “bla\nbla\”bla”))))) })))) (expr (matchable (term (binary (bil <<) (binElements (binElement (expr (exprMax (term (string "Hello"))))) , (binElement (expr (exprMax (term (char_ $\s))))) , (binElement (expr (exprMax (term (string "World!")))))) (bir >>)))) = (expr (exprMax (term (string “Hello World!”))))))))) ) 2 | -------------------------------------------------------------------------------- /test/62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/62.png -------------------------------------------------------------------------------- /test/62.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom decode) (dim ::) (tyFun ( (tyMaxs (tyMax (type (tyBinary (bil <<) (tyBinaryBase (var _) : (type (integer 64))) , (tyBinaryUnit (var _) : (var _) * (type (integer 8))) (bir >>))))) ) (lra ->) (tyMax (type (tyMap #{ (tyMapAssocs (tyMapAssoc (tyMax (type (atom id))) => (tyMax (type (tyBinary (bil <<) (tyBinaryBase (var _) : (type (integer 11))) (bir >>))))) , (tyMapAssoc (tyMax (type (atom timestamp))) => (tyMax (type (subtype (atom char) ( )))))) }))))) (func (atom decode) (args ( (matchables (matchable (term (binary (bil <<) (binElements (binElement (expr (exprMax (var _))) : (exprMax (term (integer 12)))) , (binElement (expr (exprMax (var Len))) : (exprMax (term (integer 4)))) , (binElement (expr (exprMax (var Timestamp))) : (exprMax (term (integer 16)))) , (binElement (expr (exprMax (term (integer 0)))) : (exprMax (term (integer 3)))) , (binElement (expr (exprMax (var Id))) : (exprMax (term (integer 11))) / (binType (atom bitstring))) , (binElement (expr (exprMax (term (integer 0)))) : (exprMax (term (integer 18)))) , (binElement (expr (exprMax (var Data))) : (exprMax (var Len)) / (binType (atom binary))) , (binElement (expr (expr (exprMax (var _))) (mulOp /) (expr (exprMax (term (atom binary))))))) (bir >>))))) )) (lra ->) (seqExprs (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom id)))) => (expr (exprMax (var Id)))) , (mapAssoc (expr (exprMax (term (atom data)))) => (expr (exprMax (var Data)))) , (mapAssoc (expr (exprMax (term (atom timestamp)))) => (expr (exprMax (var Timestamp))))) }))))))))) ) 2 | -------------------------------------------------------------------------------- /test/63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/63.png -------------------------------------------------------------------------------- /test/63.tree: -------------------------------------------------------------------------------- 1 | (root (block (defty (atom ty_long_var_assoc) ( ) (dim ::) (tyMax (type (subtype (atom f) ( (tyMaxs (tyMax (type (var T))) , (tyMax (type (var Q))) , (tyMax (type (var W)))) )))) when (tyGuards (tyGuard (var T) (dim ::) (var Q) (dim ::) (var W) (dim ::) (tyMax (type (subtype (atom text) ( )))))))) (block (defty (atom processor) ( ) (dim ::) (tyMax (type (fun_ fun) ( (tyFun ( (tyMaxs (tyMax (type (var T)))) ) (lra ->) (tyMax (type (var T)))) ))) when (tyGuards (tyGuard (var T) (dim ::) (tyMax (type (subtype (atom text) ( )))))))) (block (def (spec (atom analyze) (dim ::) (tyFun ( (tyMaxs (tyMax (type [ (tyMax (type (tyRecord #{ (atom document) }))) ])) , (tyMax (type [ (tyMax (type (subtype (atom processor) ( )))) ]))) ) (lra ->) (tyMax (type [ (tyMax (type (tyRecord #{ (atom tokenized_document) }))) ])))) (func (atom analyze) (args ( (matchables (matchable (var Documents)) , (matchable (var Processors))) )) (lra ->) (seqExprs (expr (functionCall (exprMax (term (atom plists))) : (exprMax (term (atom map))) (params ( (exprs (expr (fun (fun_ fun) (funClause (args ( (matchables (matchable (var Document))) )) (lra ->) (seqExprs (expr (matchable (var Words)) = (expr (functionCall (atom tokenize) (params ( (exprs (expr (exprMax (record (recRead #{ (expr (exprMax (var Document))) (atom document) (atom text) })))) , (expr (exprMax (term (string “”)))) , (expr (exprMax (term (list [ ]))))) ))))) (expr (exprMax (record (recCreateMatch #{ (atom tokenized_document) (recAssocs (recAssoc (atom url) = (expr (exprMax (record (recRead #{ (expr (exprMax (var Document))) (atom document) (atom url) }))))) , (recAssoc (atom words) = (expr (lc [ (seqExprs (expr (functionCall (atom apply_processors) (params ( (exprs (expr (exprMax (var Processors))) , (expr (exprMax (var Word)))) ))))) (gens (gen_ | (gen (matchable (var Word)) (generator <-) (expr (exprMax (var Words)))))) ])))) })))))) end)) , (expr (exprMax (var Documents)))) )))))))) (block (def (spec (fa (atom normalizer) / (integer 1)) (dim ::) (subtype (atom processor) ( ))) (func (atom normalizer) (args ( (matchables (matchable (var Word))) )) (lra ->) (seqExprs (expr (functionCall (atom processor) (params ( (exprs (expr (fun (fun_ fun) (exprMax (term (atom string))) : (exprMax (term (atom to_lower))) / (integer 1))) , (expr (exprMax (var Word)))) )))))))) (block (def (spec (atom processor) (dim ::) (tyFun ( (tyMaxs (tyMax (type (subtype (atom processor) ( )))) , (tyMax (type (var T)))) ) (lra ->) (tyMax (type (var T)))) when (tyGuards (tyGuard (var T) (dim ::) (tyMax (type (subtype (atom text) ( ))))))) (func (atom processor) (args ( (matchables (matchable (var Fun)) , (matchable (var Binary))) )) (lra ->) (seqExprs (expr (bc (bil <<) (seqExprs (expr (matchable (term (binary (bil <<) (binElements (binElement (expr (exprMax (var E))))) (bir >>)))) = (expr (exprMax (var Element)))) (expr (exprMax (term (binary (bil <<) (binElements (binElement (expr (functionCall ( (exprMax (var Fun)) (params ( (exprs (expr (exprMax (var E)))) )) ))))) (bir >>)))))) (gens (gen_ | (gen (matchable (term (binary (bil <<) (binElements (binElement (expr (exprMax (var Element))) : (exprMax (term (integer 1))) / (binType (atom binary)))) (bir >>)))) (generator <=) (expr (exprMax (var Binary)))))) (bir >>))))))) ) 2 | -------------------------------------------------------------------------------- /test/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/64.png -------------------------------------------------------------------------------- /test/64.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom record) (term (atom conf)) of (tyRecordFields { (tyRecordField (atom dirs) = (expr (exprMax (term (list [ ]))))) , (tyRecordField (atom destination) = (expr (functionCall (exprMax (term (atom filename))) : (exprMax (term (atom join))) (params ( (exprs (expr (functionCall (atom cwd) (params ( )))) , (expr (exprMax (term (string "docs/erldocs"))))) ))))) , (tyRecordField (atom includes) = (expr (exprMax (term (list [ (expr (expr (functionCall (atom cwd) (params ( )))) (listOp ++) (expr (exprMax (term (string "/include"))))) (tail , (expr (functionCall (atom cwd) (params ( )))) (tail ]))))))) , (tyRecordField (atom logfile) (dim ∷) (type (subtype (atom string) ( ))) | (type (subtype (atom binary) ( )))) }))) ) 2 | -------------------------------------------------------------------------------- /test/65.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/65.png -------------------------------------------------------------------------------- /test/65.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom f) (args ( )) (lra ->) (seqExprs (expr (if_ if (expr (if_ if (expr (unOp not) (expr (expr (exprMax (term (atom a)))) (compOp <) (expr (exprMax (term (atom b)))))) (lra ->) (expr (exprMax (term (atom a)))) if not (lra ->) (expr (exprMax (term (atom b)))))) (lra ->) (expr (exprMax (term (string "1")))) if not (lra ->) (expr (expr (exprMax (term (integer 2)))) (mulOp *) (expr (exprMax (term (atom b))))))))))) ) 2 | -------------------------------------------------------------------------------- /test/66.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/66.png -------------------------------------------------------------------------------- /test/66.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom w) (args ( )) (lra ->) (seqExprs (expr (exprMax (term (map (mapCreateMatch #{ (mapAssocs (mapAssoc (expr (exprMax (term (atom key)))) => (expr (matchable (var C)) = (expr (expr (exprMax (term (integer 1)))) (addOp +) (expr (exprMax (term (integer 2)))))))) }))))))))) ) 2 | -------------------------------------------------------------------------------- /test/67.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/67.png -------------------------------------------------------------------------------- /test/67.tree: -------------------------------------------------------------------------------- 1 | (root (block (attribute (atom module) (term (atom some_attributes)))) (block (attribute (atom behaviour) (term (atom gen_server)))) (block (attribute (atom export) (fas (fa (atom start_link) / (integer 0))))) (block (attribute (atom export) (fas (fa (atom init) / (integer 1)) (fa (atom handle_call) / (integer 3)) (fa (atom handle_cast) / (integer 2)) (fa (atom handle_info) / (integer 2)) (fa (atom terminate) / (integer 2)) (fa (atom code_change) / (integer 3))))) (block (attribute (atom record) (term (atom state)) of (term (tuple { })))) (block (def (func (atom start_link) (args ( )) (lra ->) (seqExprs (expr (functionCall (exprMax (term (atom gen_server))) : (exprMax (term (atom start_link))) (params ( (exprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom local)))) , (expr (exprMax (term (atom '?MODULE'))))) })))) , (expr (exprMax (term (atom '?MODULE')))) , (expr (exprMax (term (list [ ])))) , (expr (exprMax (term (list [ ]))))) )))))))) (block (def (func (atom init) (args ( (matchables (matchable (term (list [ ])))) )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom ok)))) , (expr (exprMax (record (recEmpty #{ (atom state) }))))) })))))))) (block (def (func (atom handle_call) (args ( (matchables (matchable (var _Request)) , (matchable (var _From)) , (matchable (var State))) )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom reply)))) , (expr (exprMax (term (atom ignored)))) , (expr (exprMax (var State)))) })))))))) (block (def (func (atom handle_cast) (args ( (matchables (matchable (var _Msg)) , (matchable (var State))) )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom noreply)))) , (expr (exprMax (var State)))) })))))))) (block (def (func (atom handle_info) (args ( (matchables (matchable (var _Info)) , (matchable (var State))) )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom noreply)))) , (expr (exprMax (var State)))) })))))))) (block (def (func (atom terminate) (args ( (matchables (matchable (var _Reason)) , (matchable (var _State))) )) (lra ->) (seqExprs (expr (exprMax (term (atom ok)))))))) (block (def (func (atom code_change) (args ( (matchables (matchable (var _OldVsn)) , (matchable (var State)) , (matchable (var _Extra))) )) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom ok)))) , (expr (exprMax (var State)))) })))))))) ) 2 | -------------------------------------------------------------------------------- /test/68.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/68.png -------------------------------------------------------------------------------- /test/68.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom count) (args ( (matchables (matchable (var Items))) )) (lra ->) (seqExprs (expr (matchable (var R)) = (expr (piped (exprMax (var Items)) (pipeOp |>) (fun (fun_ fun) (exprMax (term (atom sum))) / (integer 1)) (pipeOp |>) (fun (fun_ fun) (exprMax (term (list [ (expr (exprMax (var _))) (tail ])))) / (integer 1)) (pipeOp |>) (fun (fun_ fun) (exprMax (term (atom io))) : (exprMax (term (atom format))) (args ( (matchables (matchable (term (string "~p\n")))) )) / (integer 1))))) (expr (matchable (term (atom ok))) = (expr (exprMax (var R)))))))) ) 2 | -------------------------------------------------------------------------------- /test/69.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/69.png -------------------------------------------------------------------------------- /test/69.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom test_range_matching) (args ( (matchables (matchable (var Thing))) )) (lra ->) (seqExprs (expr (case_ case (expr (exprMax (var Thing))) (of of (clauses (clause (matchable (term (lr [ (expr (exprMax (term (integer 1)))) .. (expr (exprMax (term (integer 6)))) ]))) (lra ->) (seqExprs (expr (exprMax (term (atom coucou)))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/7.png -------------------------------------------------------------------------------- /test/7.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom t) (args ( )) (lra ->) (seqExprs (expr (try_ try (seqExprs (expr (exprMax (var Expr)))) catch (catchClauses (catchClause (exprMax (term (atom throw))) : (clause (matchable (var Term)) (lra ->) (seqExprs (expr (exprMax (var Term)))))) (catchClause (exprMax (term (atom exit))) : (clause (matchable (var Reason)) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom 'EXIT')))) , (expr (exprMax (var Reason)))) }))))))) (catchClause (exprMax (term (atom error))) : (clause (matchable (var Reason)) (lra ->) (seqExprs (expr (exprMax (term (tuple { (exprs (expr (exprMax (term (atom 'EXIT')))) , (expr (exprMax (term (tuple { (exprs (expr (exprMax (var Reason))) , (expr (functionCall (exprMax (term (atom erlang))) : (exprMax (term (atom get_stacktrace))) (params ( ))))) }))))) })))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/70.png -------------------------------------------------------------------------------- /test/70.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom test_disjoint_union_matching) (args ( (matchables (matchable (var Thing))) )) (lra ->) (seqExprs (expr (case_ case (expr (exprMax (var Thing))) (of of (clauses (clause (matchable (matchable (matchable (term (integer 1))) | (matchable (term (integer 2)))) | (matchable (term (integer 3)))) (lra ->) (seqExprs (expr (exprMax (term (atom hi)))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/71.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/71.png -------------------------------------------------------------------------------- /test/71.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom fcall_and_funs) (args ( (matchables (matchable (var F)) , (matchable (var B)) , (matchable (matchable (var N)) = (matchable (term (integer 0))))) )) (lra ->) (seqExprs (expr (functionCall (exprMax (term (atom a))) : (exprMax (var B)) (params ( (exprs (expr (exprMax (term (atom c))))) )))) (expr (exprMax ( (expr (exprMax (term (atom d)))) ))) (expr (matchable (var _FCall)) = (expr (functionCall ( (exprMax (var F)) (params ( )) )))) (expr (matchable (var _FunFCall)) = (expr (functionCall ( (exprMax ( (expr (fun (fun_ fun) (exprMax (term (atom a))) / (integer 0))) )) (params ( )) )))) (expr (matchable (var _Fun)) = (expr (fun (fun_ fun) (exprMax (term (atom a))) : (exprMax (var B)) (args ( (matchables (matchable (term (atom c)))) )) / (var N)))) (expr (exprMax (var A))) (expr (exprMax (var B))) (expr (expr (exprMax ( (expr (expr (exprMax (term (integer 1)))) (addOp +) (expr (exprMax (term (integer 2))))) ))) (mulOp *) (expr (exprMax ( (expr (expr (exprMax (term (integer 2)))) (addOp +) (expr (exprMax (term (integer 3))))) )))) (expr (exprMax (var A))) (expr (expr (functionCall ( (exprMax (var B)) (params ( (exprs (expr (expr (exprMax (term (integer 1)))) (addOp +) (expr (exprMax (term (integer 2)))))) )) ))) (mulOp *) (expr (exprMax ( (expr (expr (exprMax (term (integer 2)))) (addOp +) (expr (exprMax (term (integer 3))))) )))))))) ) 2 | -------------------------------------------------------------------------------- /test/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/72.png -------------------------------------------------------------------------------- /test/72.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (spec (atom fmap_equivalent) (dim ::) (tyFun ( (tyMaxs (tyMax (type (tyRecord #{ (atom session_context) })))) ) (lra ->) (tyMax (type (subtype (atom '') : (atom return) ( (tyMaxs (tyMax (type (tyRecord #{ (atom session_context) })))) )))))) (func (atom fmap_equivalent) (args ( (matchables (matchable (var Context))) )) (lra ->) (seqExprs (expr (case_ case (expr (piped (exprMax (var Context)) (pipeOp |>) (fun (fun_ fun) (exprMax (term (atom maybe_resource_exists))) / (integer 1)) (pipeOp -<) (fun (fun_ fun) (exprMax (term (atom maybe_validates))) / (integer 1)) (pipeOp -<) (fun (fun_ fun) (exprMax (term (atom maybe_load_something))) / (integer 1)) (pipeOp -<) (fun (fun_ fun) (exprMax (term (atom maybe_save_result))) / (integer 1)))) (of of (clauses (clause (matchable (term (tuple { (exprs (expr (exprMax (term (atom ok)))) , (expr (exprMax (var Context')))) }))) (lra ->) (seqExprs (expr (exprMax (var Context'))))) (clause (matchable (matchable (term (tuple { (exprs (expr (exprMax (term (atom error)))) , (expr (exprMax (var Reason)))) }))) = (matchable (var _E))) (lra ->) (seqExprs (expr (functionCall (exprMax (term (atom lager))) : (exprMax (term (atom error))) (params ( (exprs (expr (exprMax (term (string "handling ~s failed: ~p")))) , (expr (exprMax (term (list [ (expr (exprMax (record (recRead #{ (expr (exprMax (var Context))) (atom session_context) (atom request_id) })))) (tail , (expr (exprMax (var _E))) (tail ]))))))) )))) (expr (functionCall (exprMax (term (atom session))) : (exprMax (term (atom reply_failure))) (params ( (exprs (expr (exprMax (var Reason))) , (expr (exprMax (var Context)))) )))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/8.png -------------------------------------------------------------------------------- /test/8.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom i) (args ( (matchables (matchable (var Arg1))) )) (lra ->) (seqExprs (expr (cond cond (condClause (expr (unOp not) (expr (exprMax (var Arg1)))) (lra ->) (seqExprs (expr (exprMax (term (atom false)))))) (condClause (expr (exprMax (term (atom true)))) (lra ->) (seqExprs (expr (exprMax (term (atom false)))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/er-lang/er/50714c2327aad02aab7884552ae857780da2f2b6/test/9.png -------------------------------------------------------------------------------- /test/9.tree: -------------------------------------------------------------------------------- 1 | (root (block (def (func (atom len) (args ( (matchables (matchable (var A))) )) (lra ->) (seqExprs (expr (case_ case (expr (exprMax (var A))) (of of (clauses (clauseGuard (matchable (var L)) (guard when (expr (functionCall (atom is_list) (params ( (exprs (expr (exprMax (var L)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom length) (params ( (exprs (expr (exprMax (var L)))) )))))) (clauseGuard (matchable (var T)) (guard when (expr (functionCall (atom is_tuple) (params ( (exprs (expr (exprMax (var T)))) ))))) (lra ->) (seqExprs (expr (functionCall (atom size) (params ( (exprs (expr (exprMax (var T)))) )))))))) end)))))) ) 2 | -------------------------------------------------------------------------------- /test/check.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Looks for code separated by \n\n 4 | # , generates a PNG of the AST 5 | # , checks it against a previously generated PNG. 6 | 7 | [[ $# -eq 0 ]] && echo "Usage: [FROM=‹int≥ 1›] [T=‹path test›] $0 ‹a_file.er›" && exit 1 8 | 9 | # Enable job control 10 | set -m 11 | # http://stackoverflow.com/a/12777848/1418165 12 | 13 | function P () { 14 | printf "\e[1;3m%s\e[0m\n" "$1" 15 | } 16 | 17 | function Parse () { 18 | java org.antlr.v4.gui.TestRig Er root -encoding utf8 $* 19 | } 20 | 21 | function before_FROM? () { 22 | [[ $FROM -ne 0 ]] && [[ $1 -lt $FROM ]] 23 | } 24 | 25 | function in_EXCEPT? () { 26 | echo ${EXCEPT[@]/"$1"/"WAS_FOUND"} | grep -q "WAS_FOUND" 27 | } 28 | 29 | file="$1"; k=0 30 | 31 | T=${T:-'test'} 32 | FROM=${FROM:-1} 33 | EXCEPT=${EXCEPT[@]:-()} 34 | 35 | P "Checking '$file'. (stop by removing the generated parser, ^C won't do)." 36 | 37 | code=''; i=1 38 | while IFS='' read -r -d $'\n' line 39 | do 40 | [[ ! -f Er.tokens ]] && P 'No parser found!' && exit 2 41 | if [[ '' = "$line" ]]; then 42 | in_EXCEPT? $i && code='' && ((i++)) && continue 43 | before_FROM? $i && code='' && ((i++)) && continue 44 | P "Snippet $i:" 45 | echo "$code" 46 | ttree="$T/_$i.tree" 47 | echo "$code" | Parse -tree > "$ttree" 48 | if [[ ! -f "$T/$i.tree" ]]; then 49 | P "Add this new test under '$T/$i.{tree,png}'" 50 | cat "$ttree" 51 | echo "$code" | Parse -gui 52 | else 53 | diff -u "$T/$i.tree" "$ttree" 54 | if [[ $? -ne 0 ]]; then 55 | P " Something is wrong with test #$i" 56 | open "$T/$i.png" || exit 3 57 | echo "$code" | Parse -gui 58 | fi 59 | fi 60 | rm "$ttree" 61 | code=''; ((i++)) 62 | echo 63 | else 64 | if [[ '' = "$code" ]] 65 | then code="$line" 66 | else code="$code"$'\n'"$line" 67 | fi 68 | fi 69 | done < "$file" 70 | 71 | P "Went through all $(($i-$FROM)) tests!" 72 | --------------------------------------------------------------------------------