hacking erlang

88
Hacking Erlang building strange and magical creations http://github.com/JacobVorreuter http://jacobvorreuter.com/hacking-erlang 1

Upload: api-26318246

Post on 13-Nov-2014

1.363 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Hacking Erlang

Hacking Erlangbuilding strange and magical creations

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1

Page 2: Hacking Erlang

Things Worth Trying:•code injection

•meta programming

•reverse engineering byte code

•anything that makes Ericsson cringe...

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

2

Page 3: Hacking Erlang

Step 1understanding the abstract format

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

3

Page 4: Hacking Erlang

-module(example1). -> {attribute,LINE,module,example1}

10 -> {integer,LINE,10}

foo -> {atom,LINE,foo}

“bar” -> {string,LINE,“bar”}

[a,b,c] -> {cons,LINE,a,{cons,LINE,b,{cons,LINE,c, {nil,LINE}}}}

Some Examples

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

4

Page 5: Hacking Erlang

The Abstract Format• a tree-like structure representing parsed Erlang code• comprised of a list of forms

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

5

Page 6: Hacking Erlang

What are forms?

The Abstract Format• a tree-like structure representing parsed Erlang code• comprised of a list of forms

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

6

Page 7: Hacking Erlang

Forms are tuples that represent top-level

constructs like function declarations and

attributes

The Abstract Format• a tree-like structure representing parsed Erlang code• comprised of a list of forms

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

7

Page 8: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example1).-export([foo/0]).

foo() -> "Hello Stockholm!".

[{attribute,1,module,example1}, {attribute,3,export,[{foo,0}]}, {function,5,foo,0,[{clause,5,[],[], [{string,5,"Hello Stockholm!"}]}]}]

8

Page 9: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

[{attribute,1,module,example1}, {attribute,3,export,[{foo,0}]}, {function,5,foo,0,[{clause,5,[],[], [{string,5,"Hello Stockholm!"}]}]}]

form

-module(example1).-export([foo/0]).

foo() -> "Hello Stockholm!".

9

Page 10: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example1).-export([foo/0]).

foo() -> "Hello Stockholm!".

[{attribute,1,module,example1}, {attribute,3,export,[{foo,0}]}, {function,5,foo,0,[{clause,5,[],[], [{string,5,"Hello Stockholm!"}]}]}]

form

10

Page 11: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example1).-export([foo/0]).

foo() -> "Hello Stockholm!".

[{attribute,1,module,example1}, {attribute,3,export,[{foo,0}]}, {function,5,foo,0,[{clause,5,[],[], [{string,5,"Hello Stockholm!"}]}]}]

form

11

Page 12: Hacking Erlang

Where do forms come from?Taking a step back:

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

12

Page 13: Hacking Erlang

Forms are generated by grouping and interpreting tokens scanned from source code.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

13

Page 14: Hacking Erlang

How does scanning source code work?

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

14

Page 15: Hacking Erlang

How does scanning source code work?

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

•use regular expressions to tokenize string input

•generate a list of tuples, each representing an atomic unit of source code, which can be grouped into forms

15

Page 16: Hacking Erlang

erl_scan

This module contains functions for tokenizing characters into Erlang tokens.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

16

Page 17: Hacking Erlang

erl_scan

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Code = "-module(example1).\n-export([foo/0]).\n\n foo() -> \"Hello Stockholm!\". "

2> erl_scan:string(Code).

-module(example1).-export([foo/0]).

foo() -> "Hello Stockholm!".

17

Page 18: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

2> erl_scan:string(Code).{ok,[{'-',1}, {atom,1,module}, {'(',1}, {atom,1,example1}, {')',1}, {dot,1}, {'-',2}, {atom,2,export}, {'(',2}, {'[',2}, {atom,2,foo}, {'/',2}, {integer,2,0}, {']',2}, {')',2}, {dot,2}, {atom,4,foo}, {'(',4}, {')',4}, {'->',4}, {string,4,"Hello Stockholm!"}, {dot,4}], 4}

-module(example1).

-export([foo/0]).

foo() -> "Hello Stockholm!".

18

Page 19: Hacking Erlang

erl_parse

This module is the basic Erlang parser which converts tokens into the abstract format.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

19

Page 20: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example1).

2> erl_scan:string(Code).{ok,[{'-',1}, {atom,1,module}, {'(',1}, {atom,1,example1}, {')',1}, {dot,1}, {'-',2}, {atom,2,export}, {'(',2}, {'[',2}, {atom,2,foo}, {'/',2}, {integer,2,0}, {']',2}, {')',2}, {dot,2}, {atom,4,foo}, {'(',4}, {')',4}, {'->',4}, {string,4,"Hello Stockholm!"}, {dot,4}], 4}

1> erl_parse:parse_form([{'-',1},{atom,1,module},{'(',1},{atom,1,example1},{')',1},{dot,1}]).

{ok,{attribute,1,module,example1}}

20

Page 21: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-export([foo/0]).

2> erl_scan:string(Code).{ok,[{'-',1}, {atom,1,module}, {'(',1}, {atom,1,example1}, {')',1}, {dot,1}, {'-',2}, {atom,2,export}, {'(',2}, {'[',2}, {atom,2,foo}, {'/',2}, {integer,2,0}, {']',2}, {')',2}, {dot,2}, {atom,4,foo}, {'(',4}, {')',4}, {'->',4}, {string,4,"Hello Stockholm!"}, {dot,4}], 4}

2> erl_parse:parse_form([{'-',2},{atom,2,export},{'(',2},{'[',2},{atom,2,foo},{'/',2},{integer,2,0},{']',2},{')',2},{dot,2}]).

{ok,{attribute,2,export,[{foo,0}]}}

21

Page 22: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

3> erl_parse:parse_form([{atom,4,foo},{'(',4},{')',4},{'->',4},{string,4,"Hello Stockholm!"},{dot,4}]).

{ok,{function,4,foo,0, [{clause,4,[],[],[{string, 4,"Hello Stockholm!"}]}]}}

2> erl_scan:string(Code).{ok,[{'-',1}, {atom,1,module}, {'(',1}, {atom,1,example1}, {')',1}, {dot,1}, {'-',2}, {atom,2,export}, {'(',2}, {'[',2}, {atom,2,foo}, {'/',2}, {integer,2,0}, {']',2}, {')',2}, {dot,2}, {atom,4,foo}, {'(',4}, {')',4}, {'->',4}, {string,4,"Hello Stockholm!"}, {dot,4}], 4}

foo() -> "Hello Stockholm!".

22

Page 23: Hacking Erlang

compile

This module provides an interface to the standard Erlang compiler. It can generate either a new file which contains the object code, or return a binary which can be loaded directly.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

23

Page 24: Hacking Erlang

compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Forms = [{attribute,1,module,example1},{attribute,2,export,[{foo,0}]},{function,4,foo,0,[{clause,4,[],[],

[{string,4,"Hello Stockholm!"}]}]}].

24

Page 25: Hacking Erlang

compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Forms = [{attribute,1,module,example1},{attribute,2,export,[{foo,0}]},{function,4,foo,0,[{clause,4,[],[],

[{string,4,"Hello Stockholm!"}]}]}].

2> {ok, Mod, Bin} = compile:forms(Forms, []).{ok,example1,<<70,79,82,49,...>>}

25

Page 26: Hacking Erlang

compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Forms = [{attribute,1,module,example1},{attribute,2,export,[{foo,0}]},{function,4,foo,0,[{clause,4,[],[],

[{string,4,"Hello Stockholm!"}]}]}].

2> {ok, Mod, Bin} = compile:forms(Forms, []).{ok,example1,<<70,79,82,49,...>>}

3> code:load_binary(Mod, [], Bin).{module,example1}

26

Page 27: Hacking Erlang

compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Forms = [{attribute,1,module,example1},{attribute,2,export,[{foo,0}]},{function,4,foo,0,[{clause,4,[],[],

[{string,4,"Hello Stockholm!"}]}]}].

2> {ok, Mod, Bin} = compile:forms(Forms, []).{ok,example1,<<70,79,82,49,...>>}

3> code:load_binary(Mod, [], Bin).{module,example1}

4> example1:foo()."Hello Stockholm!"

27

Page 28: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

28

Page 29: Hacking Erlang

dynamic_compile

The dynamic_compile module performs the actions we’ve just seen, plus takes care of macro expansion and inclusion of external header files.

http://github.com/JacobVorreuter/dynamic_compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

29

Page 30: Hacking Erlang

dynamic_compile

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Code = "-module(example1)...".

2> dynamic_compile:load_from_string(Code).{module,example1}

3> example1:foo()."Hello Stockholm!"

30

Page 31: Hacking Erlang

moving on...

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

31

Page 32: Hacking Erlang

the parse_transform debate...

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

32

Page 33: Hacking Erlang

Programmers are strongly advised NOT

to engage in parse transformations

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

33

Page 34: Hacking Erlang

yeah, you can do everything with

macros anyway

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

34

Page 35: Hacking Erlang

wait! parse_transforms are cool and have their place in the

language...in moderation.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

35

Page 36: Hacking Erlang

If the option {parse_transform, Module} is passed to the compiler, a user written function parse_transform/2 is called by the compiler before the code is checked for errors.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

How do parse_transforms work?

36

Page 37: Hacking Erlang

How do parse_transforms work?

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(print_forms).-export([parse_transform/2]).

parse_transform(Forms, _Options) -> io:format("forms: ~p~n", [Forms]), Forms.

-module(example1).-compile({parse_transform, print_forms}).-export([foo/0]).

foo() -> "Hello Stockholm!".

37

Page 38: Hacking Erlang

How do parse_transforms work?

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

jvorreuter$ erlc -o ebin src/print_forms.erl

jvorreuter$ erlc -o ebin -pa ebin src/example1.erl forms: [{attribute,1,file,{"src/example1.erl",1}}, {attribute,1,module,example1}, {attribute,3,export,[{foo,0}]}, {function,5,foo,0,[{clause,5,[],[],[{string,

5,"Hello Stockholm!"}]}]}, {eof,8}]

38

Page 39: Hacking Erlang

#pizza{ size = "large", toppings = ["onions", "peppers", "olives"], price = "$14.99"}

[{size, "large"}, {toppings, ["onions", "peppers", "olives"]}, {price, "$14.99"}]

a pizza example

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

encode pizza

39

Page 40: Hacking Erlang

#pizza{ size = "large", toppings = ["onions", "peppers", "olives"], price = "$14.99"}

[{size, "large"}, {toppings, ["onions", "peppers", "olives"]}, {price, "$14.99"}]

a pizza example

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

record_to_proplist(#pizza{})

40

Page 41: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example2).-export([record_to_proplist/1]). -record(pizza, {size, toppings, price}). record_to_proplist(Rec) when is_tuple(Rec) -> case Rec of Pizza when is_record(Pizza, pizza) -> [{size, Pizza#pizza.size}, {toppings, Pizza#pizza.toppings}, {price, Pizza#pizza.price}]; _ -> exit(wtf_do_i_do_with_this) end.

manually assemble proplist with correct fields

41

Page 42: Hacking Erlang

remember, at runtime all references to record instances have been replaced with indexed tuples.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

42

Page 43: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example2).-compile({parse_transform, expand_records}).-export([record_to_proplist/1]). -record(pizza, {size, toppings, price}). record_to_proplist(Rec) when is_tuple(Rec) -> [RecName|FieldValues] = tuple_to_list(Rec), FieldNames = expanded_record_fields(RecName), lists:zip(FieldNames, FieldValues).

this function is injected into the precompiled forms

during the parse_transform

43

Page 44: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> Pizza = #pizza{size="large", toppings=["onions", "peppers", "olives"], price="$14.99"}.

2> example2:record_to_proplist(Pizza).[{size,"large"}, {toppings,["onions","peppers","olives"]}, {price,"$14.99"}]

44

Page 45: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

a look at expand_records.erl

45

Page 46: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

two accumulators:list of forms to return to the compiler and a list of record definitions that we will use to

build our injected function

46

Page 47: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

accumulate record attributes

47

Page 48: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

once all record attributes have been collected we use them to construct

our function

48

Page 49: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

49

Page 50: Hacking Erlang

intermission

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

50

Page 51: Hacking Erlang

Act IIcompiling custom syntax

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

51

Page 52: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

➸ dingbats ✂

➽ numbers ✓ ◤ 1 ➟ 16 ✈ ❤ ◥ ✂

52

Page 53: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

➸ dingbats ✂

➽ numbers ✓ ◤ 1 ➟ 16 ✈ ❤ ◥ ✂

1> dingbats:numbers().[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

53

Page 54: Hacking Erlang

leex - A regular expression based lexical analyzer generator for Erlang, similar to lex or flex.

yecc - An LALR-1 parser generator for Erlang, similar to yacc.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

54

Page 55: Hacking Erlang

leexThe leex module takes a definition file with the extension .xrl as input and generates the source code for a lexical analyzer as output.

< Definitions >< Rules ><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

55

Page 56: Hacking Erlang

example_scanner.xrl< Definitions >< Rules ><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

56

Page 57: Hacking Erlang

Definitions.A = [a-z][0-9a-zA-Z_]*I = [0-9]+WS = ([\000-\s]|%.*)

< Rules ><Erlang Code>

example_scanner.xrl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

57

Page 58: Hacking Erlang

Definitions.A = [a-z][0-9a-zA-Z_]*I = [0-9]+WS = ([\000-\s]|%.*)

Rules.\➸ : {token,{module,TokenLine}}.\➽ : {token,{function,TokenLine}}.\✓ : {token,{'->',TokenLine}}.\◤ : {token,{'[',TokenLine}}.\◥ : {token,{']',TokenLine}}.{A} : {token,{atom,TokenLine,list_to_atom(TokenChars)}}.{I} : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.\➟ : {token,{'<-',TokenLine}}.\✈ : {token,{'||',TokenLine}}.\❤ : {token,{heart,TokenLine}}.\✂{WS} : {end_token,{dot,TokenLine}}.{WS}+ : skip_token.

<Erlang Code>

example_scanner.xrl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

58

Page 59: Hacking Erlang

Definitions.A = [a-z][0-9a-zA-Z_]*I = [0-9]+WS = ([\000-\s]|%.*)

Rules.\➸ : {token,{module,TokenLine}}.\➽ : {token,{function,TokenLine}}.\✓ : {token,{'->',TokenLine}}.\◤ : {token,{'[',TokenLine}}.\◥ : {token,{']',TokenLine}}.{A} : {token,{atom,TokenLine,list_to_atom(TokenChars)}}.{I} : {token,{integer,TokenLine,list_to_integer(TokenChars)}}.\➟ : {token,{'<-',TokenLine}}.\✈ : {token,{'||',TokenLine}}.\❤ : {token,{heart,TokenLine}}.\✂{WS} : {end_token,{dot,TokenLine}}.{WS}+ : skip_token.

Erlang code.

example_scanner.xrl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

59

Page 60: Hacking Erlang

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> leex:file("src/example_scanner.xrl").{ok,"src/example_scanner.erl"}

example_scanner.xrl

60

Page 61: Hacking Erlang

yecc

* Backus–Naur Form (BNF) is a metasyntax used to express context-free grammars: that is, a formal way to describe formal languages

The yecc module takes a BNF* grammar definition as input, and produces the source code for a parser.

<Header><Non-terminals><Terminals><Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

61

Page 62: Hacking Erlang

example_parse.yrl<Header><Non-terminals><Terminals><Root Symbol><End Symbol><Erlang Code>

The header provides a chance to add

documentation before the module declaration

in your parser

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

62

Page 63: Hacking Erlang

example_parse.yrlHeader "%% Copyright (C)""%% @Author Jacob Vorreuter"

<Non-terminals><Terminals><Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

We could do something like

this, but whatever

63

Page 64: Hacking Erlang

example_parse.yrl<Non-terminals><Terminals><Root Symbol><End Symbol><Erlang Code>

Terminal symbols are literal strings forming the input of a formal grammar and cannot be broken down into smaller

units without losing their literal meaning

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

64

Page 65: Hacking Erlang

example_parse.yrl<Non-terminals>

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

<Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

65

Page 66: Hacking Erlang

example_parse.yrl<Non-terminals>

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

<Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

These terminal symbols are the products of the regular

expressions in our lexical analyzer

66

Page 67: Hacking Erlang

example_parse.yrl<Non-terminals>

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

<Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

\➸ : {token,{module,TokenLine}}.\➽ : {token,{function,TokenLine}}.\✓ : {token,{'->',TokenLine}}.\◤ : {token,{'[',TokenLine}}.

\◥ : {token,{']',TokenLine}}.

{A} : {token,{atom,TokenLine, list_to_atom(TokenChars)}}.{I} : {token,{integer,TokenLine, list_to_integer(TokenChars)}}.\➟ : {token,{'<-',TokenLine}}.\✈ : {token,{'||',TokenLine}}.\❤ : {token,{heart,TokenLine}}.\✂{WS} : {end_token,{dot,TokenLine}}.{WS}+ : skip_token.

67

Page 68: Hacking Erlang

example_parse.yrl<Non-terminals>

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

<Root Symbol><End Symbol><Erlang Code> Nonterminal symbols are the

rules within the formal grammar consisting of a sequence of

terminal symbols or nonterminal symbols. Nonterminal symbols

may self reference to specify recursion.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

68

Page 69: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.<Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

69

Page 70: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.

Terminals atom integer heart module function '[' ']' '->' '<-' '||'.<Root Symbol><End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

Here we are declaring symbols that will be further defined as

descendants of the root symbol

70

Page 71: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.<Root Symbol><End Symbol><Erlang Code> The root symbol is the most

general syntactic category which the parser ultimately will parse

every input string into.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

71

Page 72: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

Rootsymbol element.

<End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

72

Page 73: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.

<End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

the dollar sign notation references the value of the indexed item on the right

side of the symbol definition

73

Page 74: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.

<End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

since our example module is named dingbats, the value of ‘$2’ in this case will be

{atom,LINE,dingbats}

74

Page 75: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].

<End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

75

Page 76: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.

Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].comprehension -> '[' ']' : nil.comprehension -> '[' integer '<-' integer '||' heart ']' :

{lc,line_of('$2'),{var,line_of('$2'),'A'},[{generate,line_of('$2'),{var,line_of('$2'),'A'},{call,line_of('$2'),{remote,line_of('$2'),{atom,line_of('$2'),lists},{atom,line_of('$2'),seq}},['$2','$4']}}]}.

<End Symbol><Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

76

Page 77: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].comprehension -> '[' ']' : nil.comprehension -> '[' integer '<-' integer '||' heart ']' :

{lc,line_of('$2'),{var,line_of('$2'),'A'},[{generate,line_of('$2'),{var,line_of('$2'),'A'},{call,line_of('$2'),{remote,line_of('$2'),{atom,line_of('$2'),lists},{atom,line_of('$2'),seq}},['$2','$4']}}]}.

<End Symbol><Erlang Code>

the end symbol is a declaration of the end_of_input symbol that your scanner is expected to use.

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

77

Page 78: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].comprehension -> '[' ']' : nil.comprehension -> '[' integer '<-' integer '||' heart ']' :

{lc,line_of('$2'),{var,line_of('$2'),'A'},[{generate,line_of('$2'),{var,line_of('$2'),'A'},{call,line_of('$2'),{remote,line_of('$2'),{atom,line_of('$2'),lists},{atom,line_of('$2'),seq}},['$2','$4']}}]}.

Endsymbol dot.

<Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

78

Page 79: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].comprehension -> '[' ']' : nil.comprehension -> '[' integer '<-' integer '||' heart ']' :

{lc,line_of('$2'),{var,line_of('$2'),'A'},[{generate,line_of('$2'),{var,line_of('$2'),'A'},{call,line_of('$2'),{remote,line_of('$2'),{atom,line_of('$2'),lists},{atom,line_of('$2'),seq}},['$2','$4']}}]}.

Endsymbol dot.

<Erlang Code>

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

The Erlang code section can contain any functions that we need to call

from our symbol definitions

79

Page 80: Hacking Erlang

example_parse.yrlNonterminals element module_declaration function_declaration function_body comprehension.Terminals atom integer heart module function '[' ']' '->' '<-' '||'.Rootsymbol element.element -> module_declaration : '$1'.element -> function_declaration : '$1'.module_declaration -> module atom :

{attribute,line_of('$2'),module,value_of('$2')}.function_declaration -> function atom '->' function_body :

{function,line_of('$2'),value_of('$2'),0,[{clause,line_of('$2'),[],[],'$4'}]}.function_body -> comprehension : ['$1'].comprehension -> '[' ']' : nil.comprehension -> '[' integer '<-' integer '||' heart ']' :

{lc,line_of('$2'),{var,line_of('$2'),'A'},[{generate,line_of('$2'),{var,line_of('$2'),'A'},{call,line_of('$2'),{remote,line_of('$2'),{atom,line_of('$2'),lists},{atom,line_of('$2'),seq}},['$2','$4']}}]}.

Endsymbol dot.

Erlang code.value_of(Token) -> element(3, Token).line_of(Token) -> element(2, Token).

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

80

Page 81: Hacking Erlang

example_parse.yrl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

2> yecc:file("src/example_parser.yrl", []).{ok,"src/example_parser.erl"}

jvorreuter$ erlc -o ebin src/*.erl

81

Page 82: Hacking Erlang

example_parse.yrl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

1> example4:compile_and_load("src/dingbats").{module,dingbats}

2> dingbats:numbers().[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]

82

Page 83: Hacking Erlang

example4.erl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

-module(example4).-export([compile_and_load/1]).

compile_and_load(Path) -> {ok, Bin} = file:read_file(Path), [Form|Forms] = scan_parse([], binary_to_list(Bin), 0, []), Forms1 = [Form,{attribute,1,compile,export_all}|Forms], {ok, Mod, Bin1} = compile:forms(Forms1, []), code:load_binary(Mod, [], Bin1).scan_parse(Cont, Str, StartLoc, Acc) -> case example_scanner:tokens(Cont, Str, StartLoc) of {done, {ok, Tokens, EndLoc}, LeftOverChars} -> {ok, Form} = example_parser:parse(Tokens), scan_parse([], LeftOverChars, EndLoc, [Form|Acc]); _ -> lists:reverse(Acc) end.

read the contents of the source file

83

Page 84: Hacking Erlang

-module(example4).-export([compile_and_load/1]).

compile_and_load(Path) -> {ok, Bin} = file:read_file(Path), [Form|Forms] = scan_parse([], binary_to_list(Bin), 0, []), Forms1 = [Form,{attribute,1,compile,export_all}|Forms], {ok, Mod, Bin1} = compile:forms(Forms1, []), code:load_binary(Mod, [], Bin1).scan_parse(Cont, Str, StartLoc, Acc) -> case example_scanner:tokens(Cont, Str, StartLoc) of {done, {ok, Tokens, EndLoc}, LeftOverChars} -> {ok, Form} = example_parser:parse(Tokens), scan_parse([], LeftOverChars, EndLoc, [Form|Acc]); _ -> lists:reverse(Acc) end.

example4.erl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

tokenize the source code using our custom scanner

84

Page 85: Hacking Erlang

-module(example4).-export([compile_and_load/1]).

compile_and_load(Path) -> {ok, Bin} = file:read_file(Path), [Form|Forms] = scan_parse([], binary_to_list(Bin), 0, []), Forms1 = [Form,{attribute,1,compile,export_all}|Forms], {ok, Mod, Bin1} = compile:forms(Forms1, []), code:load_binary(Mod, [], Bin1).scan_parse(Cont, Str, StartLoc, Acc) -> case example_scanner:tokens(Cont, Str, StartLoc) of {done, {ok, Tokens, EndLoc}, LeftOverChars} -> {ok, Form} = example_parser:parse(Tokens), scan_parse([], LeftOverChars, EndLoc, [Form|Acc]); _ -> lists:reverse(Acc) end.

example4.erl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

parse the tokens into forms using our custom parser

85

Page 86: Hacking Erlang

-module(example4).-export([compile_and_load/1]).

compile_and_load(Path) -> {ok, Bin} = file:read_file(Path), [Form|Forms] = scan_parse([], binary_to_list(Bin), 0, []), Forms1 = [Form,{attribute,1,compile,export_all}|Forms], {ok, Mod, Bin1} = compile:forms(Forms1, []), code:load_binary(Mod, [], Bin1).scan_parse(Cont, Str, StartLoc, Acc) -> case example_scanner:tokens(Cont, Str, StartLoc) of {done, {ok, Tokens, EndLoc}, LeftOverChars} -> {ok, Form} = example_parser:parse(Tokens), scan_parse([], LeftOverChars, EndLoc, [Form|Acc]); _ -> lists:reverse(Acc) end.

example4.erl

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

compile and load the binary

86

Page 87: Hacking Erlang

custom syntax in the wild...

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

• Lisp Flavored Erlang

• Prolog Interpreter for Erlang

• Erlang implementation of the Django Template Language

• Reia - a Ruby/Python-like scripting language for the Erlang VM

87

Page 88: Hacking Erlang

END

http://github.com/JacobVorreuterhttp://jacobvorreuter.com/hacking-erlang

88