SlideShare a Scribd company logo
1 of 63
Download to read offline
elixir
!
"
#


Interactive Elixir (1.1.0)
iex(1)> import Paco
nil
iex(2)> import Paco.Parser
nil
iex(3)> parse("a", lit("a"))
{:ok, "a"}
iex(4)> parse("a", lit("a"), format: :raw)
%Paco.Success{
from: {0, 1, 1},
to: {0, 1, 1},
at: {1, 1, 2},
result: "a",
tail: "",
...}
iex(5)> parse("aaa", lit("aaa"))
{:ok, "aaa"}
iex(6)> "aaa" |> parse(lit("aaa"))
{:ok, "aaa"}
iex(7)> "aaa" |> parse(lit("a"))
{:ok, "a"}
iex(8)> "aaa" |> parse(lit("a"), format: :raw)
%Paco.Success{
from: {0, 1, 1},
to: {0, 1, 1},
at: {1, 1, 2},
result: "a",
tail: "aa",
...}
iex(9)> "b" |> parse(lit("a"))
{:error, "expected "a" at 1:1 but got "b""}
iex(10)> "b" |> parse(lit("a"), format: :raw)
%Paco.Failure{
at: {0, 1, 1},
expected: "a",
tail: "b",
...}
iex(1)> "aaa" |> parse(any)
{:ok, "a"}
iex(2)> "aaa" |> parse(any(1))
{:ok, "a"}
iex(3)> "aaa" |> parse(any(2))
{:ok, "aa"}
iex(4)> "a" |> parse(any(2))
{:error, "expected exactly 2 characters at 1:1 but got "a""}
iex(5)> "aaa" |> parse(any(at_least: 2))
{:ok, "aaa"}
iex(6)> "aaa" |> parse(any(at_most: 2))
{:ok, "aa"}
iex(1)> "bbabcd" |> parse(while("abc"))
{:ok, "bbabc"}
iex(2)> "xxx" |> parse(while("abc"))
{:ok, ""}
iex(3)> "xxx" |> parse(while("abc", at_least: 2))
{:error,
"expected at least 2 characters in alphabet "abc" at 1:1
but got "xx""}
iex(4)> import Paco.ASCII, only: [lowercase?: 1]
iex(5)> "abCD" |> parse(while(&lowercase?/1))
{:error, "ab"}
iex(6)> "abCD" |> parse(while(&lowercase?/1, at_least: 3))
{:error,
"expected at least 3 lowercase characters at 1:1
but got "abC""}
iex(1)> "abc" |> parse(until("c"))
{:ok, "ab"}
iex(2)> "abcdc" |> parse(until("c", escaped_with: ""))
{:ok, "abcd"}
iex(3)> "abcdc" |> parse(until("c", escaped_with: "",
keep_escape: true))
{:ok, "abcd"}
iex(4)> "abc" |> parse(until("d"))
{:error, "expected something ended by "d" at 1:1
but got "abc""}
iex(5)> "abc" |> parse(until("d"), eof: true)
{:error, "abc"}
iex(1)> "ab" |> parse(sequence_of([lit("a"), lit("b")]))
{:ok, ["a", "b"]}
iex(2)> "ac" |> parse(sequence_of([lit("a"), lit("b")]))
{:error, "expected "b" at 1:2 but got "c""}}
iex(3)> ab = sequence_of([lit("a"), lit("b")])
%Paco.Parser{...}
iex(4)> "abc" |> parse(sequence_of([ab, lit("c")]))
{:ok, [["a", "b"], "c"]}
iex(5)> "xxx" |> parse(sequence_of([ab, lit("c")]))
{:error, "expected "a" at 1:1 but got "x""}
iex(6)> "axx" |> parse(sequence_of([ab, lit("c")]))
{:error, "expected "b" at 1:2 but got "x""}
iex(7)> "abx" |> parse(sequence_of([ab, lit("c")]))
{:error, "expected "c" at 1:3 but got "x""}
iex(1)> "a" |> parse(one_of([lit("a"), lit("b")]))
{:ok, "a"}
iex(2)> "b" |> parse(one_of([lit("a"), lit("b")]))
{:ok, "b"}
iex(4)> # farthest failure (higher rank) wins
nil
iex(3)> "ab" |> parse(one_of([lit("ac"), lit("bc")]))
{:error, "expected "ac" at 1:1 but got "ab""}
iex(6)> # failures with same rank are composed
nil
iex(5)> "ab" |> parse(one_of([lit("ac"), lit("ad")]))
{:error, "expected one of ["ac", "ad"] at 1:1
but got "ab""}
iex(1)> "aaa" |> parse(repeat(lit("a")))
{:ok, ["a", "a", "a"]}
iex(2)> "aaa" |> parse(repeat(lit("a"), 2))
{:ok, ["a", "a"]}
iex(4)> "aaa" |> parse(repeat(lit("a"), at_most: 2))
{:ok, ["a", "a"]}
iex(3)> "aaa" |> parse(repeat(lit("a"), at_least: 4))
{:error, ""expected "a" at 1:2 but got the end of input"}
iex(6)> "abba" |> parse(repeat(one_of([lit("a"), lit("b")])))
{:ok, ["a", "b", "b", "a"]}
defmodule Paco.ASCII do
!
@upper ["A","B","C","D","E",...,"Z"]
!
@classes [... {:upper, :upper?, @upper}, ...]
for {class, is_class, elements} <- @classes do
def unquote(class)(), do: unquote(elements)
for element <- elements do
def unquote(is_class)(<<unquote(element)>>), do: true
end
def unquote(is_class)(_), do: false
end
!
# def upper, do: @upper
# def upper?("A"), do: true
# def upper?("B"), do: true
# ...
# def upper?(_), do: false
ws = while(ASCII.ws)
!
hello = lit("Hello")
separator = sequence_of([ws, lit(","), ws])
what = while(ASCII.letter, at_least: 1)
terminator = sequence_of([ws, lit("!")])
!
greetings = sequence_of([hello, separator, what, terminator])
!
parse("Hello,World!", greetings) |> IO.inspect
# {:ok, ["Hello", ["", ",", ""], "World", ["", "!"]]}
!
parse("Hello, BEAM!", greetings) |> IO.inspect
# {:ok, ["Hello", ["", ",", " "], "BEAM", ["", "!"]]}
# Good, not great: skip everything that is not interesting
!
ws = while(ASCII.ws)
!
hello = lit("Hello") |> skip
separator = sequence_of([ws, lit(","), ws]) |> skip
what = while(ASCII.letter, at_least: 1)
terminator = sequence_of([ws, lit("!")]) |> skip
!
greetings = sequence_of([hello, separator, what, terminator])
!
parse("Hello,World!", greetings) |> IO.inspect
# {:ok, ["World"]}
!
parse("Hello, BEAM!", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
# Not everyone are so loud, `!` should be optional
!
ws = while(ASCII.ws)
!
hello = lit("Hello") |> skip
separator = sequence_of([ws, lit(","), ws]) |> skip
what = while(ASCII.letter, at_least: 1)
terminator = sequence_of([ws, lit("!")]) |> maybe
!
greetings = sequence_of([hello, separator, what, terminator])
!
parse("Hello, BEAM!", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
!
parse("Hello, BEAM", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
# Let's get rid of non significant whitespaces with lex(s)
!
# In module Paco.Parser...
!
parser lex(s),
as: lit(s) |> surrounded_by(maybe(whitespaces))
!
parser surrounded_by(p, left, right),
as: sequence_of([skip(left), p, skip(right)])
# Use lex Luke!
!
ws = while(ASCII.ws)
!
hello = lit("Hello") |> skip
what = while(ASCII.letter, at_least: 1)
separator = lex(",") |> skip
terminator = lex("!") |> maybe
!
greetings = sequence_of([hello, separator, what, terminator])
!
parse("Hello, BEAM!", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
!
parse("Hello, BEAM", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
# It's common to have something non significant
# that follows or precedes something significant
!
# In module Paco.Parser...
!
parser followed_by(p, right),
as: sequence_of([p, skip(right)])
!
parser preceded_by(p, right),
as: sequence_of([skip(left), p])
# An alternative and shorter version
!
what = while(ASCII.letter, at_least: 1)
!
greetings = what
|> preceded_by(lit("Hello")
|> followed_by(lex(",")))
|> followed_by(maybe(lex("!")))
!
!
parse("Hello, BEAM!", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
!
parse("Hello, BEAM", greetings) |> IO.inspect
# {:ok, ["BEAM"]}
# Parse a sequence of numbers separated by `+` or `-`
!
number = while(ASCII.digit, at_least: 1)
operator = one_of([lex("+"), lex("-")])
!
expression = number |> separated_by(operator)
!
parse("1", expression) |> IO.inspect
# {:ok, ["1"]}
!
parse("1 + 2", expression) |> IO.inspect
# {:ok, ["1", "2"]}
!
parse("1 + 2 - 3", expression) |> IO.inspect
# {:ok, ["1", "2", "3"]}
!
# Small problem... to compute the value we need the operators!
# Parse a sequence of numbers separated by `+` or `-`
!
number = while(ASCII.digit, at_least: 1)
operator = one_of([lex("+"), lex("-")])
!
expression = number |> separated_by(keep(operator))
!
parse("1", expression) |> IO.inspect
# {:ok, ["1"]}
!
parse("1 + 2", expression) |> IO.inspect
# {:ok, ["1", "+", "2"]}
!
parse("1 + 2 - 3", expression) |> IO.inspect
# {:ok, ["1", "+", "2", "-", "3"]}
!
# Ok, but we need numbers not strings
# In module Paco.Parser...
parser bind(p, f) do
fn state, _ ->
case p.parse.(state, p) do
%Success{result: result} = success ->
case f.(result, success) do
%Failure{} = failure ->
failure
%Success{} = success ->
success
result ->
%Success{success|result: result}
end
%Failure{} = failure ->
failure
end
end
end
# Parse a sequence of numbers separated by `+` or `-`
!
number = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
!
operator = one_of([lex("+"), lex("-")])
!
expression = number |> separated_by(keep(operator))
!
parse("1 + 2 - 3", expression) |> IO.inspect
# {:ok, [1, "+", 2, "-", 3]}
!
# Missing only the last step... compute the result :-)
# Parse a sequence of numbers separated by `+` or `-`
!
number = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
!
operator = one_of([lex("+"), lex("-")])
!
expression = number
|> separated_by(keep(operator))
|> bind(&Paco.Transform.separated_by(&1,
fn("+", n, m) -> n + m
("-", n, m) -> n - m
end))
!
!
parse("1 + 2 - 3", expression) |> IO.inspect
# {:ok, 0]}
# Parse a{n}b{n}c{n} where n ∈ ℕ
!
# If you knew the `n` (ex. 3) it would be easy
!
p = sequence_of([while("a", 3), while("b", 3), while("c", 3)])
!
parse("aaabbbccc", p) |> IO.inspect
# {:ok, ["aaa", "bbb", "ccc"]}
!
# We need to be able to peek ahead and then create a parser
# with that knowledge
# In module Paco.Parser...
!
parser peek(box(p)) do
fn %State{at: at, text: text} = state, _ ->
case p.parse.(state, p) do
%Success{result: result} ->
%Success{from: at, to: at, at: at,
tail: text,
result: result}
%Failure{} = failure ->
failure
end
end
end
# In module Paco.Parser...
!
parser then(p, f) when is_function(f), as:
bind(p, f)
|> bind(fn(p, _, s) -> p.parse.(s, p) end)
# Parse a{n}b{n}c{n} where n ∈ ℕ
!
p = peek(while("a"))
|> then(fn(a) ->
len = String.length(a)
sequence_of([while("a", len),
while("b", len),
while("c", len)])
end)
!
parse("aaabbbccc", p) |> IO.inspect
# {:ok, ["aaa", "bbb", "ccc"]}
!
parse("aaabbccc", p) |> IO.inspect
# {:error,
"expected exactly 3 characters in alphabet "b" at 1:4
but got "bbc""}
# An `element` is a word beginning with one uppercase letter
# followed by zero or more lowercase letters
element = sequence_of([while(ASCII.upper, 1),
while(ASCII.lower)])
!
# A `quantity` is a number greater than zero
# If the quantity is omitted assume the value of 1 as default
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
!
# A `reference` is an element optionally followed by a quantity
reference = sequence_of([element, quantity])
!
formula = repeat(reference, at_least: 1)
parse("H2O", formula) |> IO.inspect
# {:ok, [[["H", ""], 2], [["O", ""], 1]]}
!
# That's right but the output format sucks!
!
# What we really want is something like
# {:ok, [%{element: "H", quantity: 2},
%{element: "0", quantity: 1}]
!
# Is that possible???
defprotocol Paco.Parsable do
@moduledoc """
A protocol that converts terms into Paco parsers
"""
@fallback_to_any true
@doc """
Returns a parser that parses `t` and keeps the shape of `t`
"""
@spec to_parser(t) :: Paco.Parser.t
def to_parser(t)
end
defimpl Paco.Parsable, for: BitString do
import Paco.Parser
def to_parser(s) when is_binary(s) do
lit(s)
end
def to_parser(s) do
raise Protocol.UndefinedError, protocol: @protocol, value: s
end
end
iex(1)> "aaa" |> parse(lit("aaa"))
{:ok, "aaa"}
iex(2)> "aaa" |> parse("aaa")
{:ok, "aaa"}
defimpl Paco.Parsable, for: List do
import Paco.Parser
def to_parser(l) do
sequence_of(l)
end
end
iex(1)> "ab" |> parse(sequence_of([lit("a"), lit("b")]))
{:ok, ["a", "b"]}
iex(2)> "ab" |> parse(sequence_of(["a", "b"]))
{:ok, ["a", "b"]}
iex(3)> "ab" |> parse(["a", "b"])
{:ok, ["a", "b"]}
defimpl Paco.Parsable, for: Tuple do
import Paco.Parser
def to_parser(tuple) do
sequence_of(Tuple.to_list(tuple))
|> bind(&List.to_tuple/1)
end
end
iex(1)> "ab" |> parse({"a", "b"}))
{:ok, {"a", "b"}}
defimpl Paco.Parsable, for: Map do
import Paco.Parser
def to_parser(tuple) do
{keys, values} = {Map.keys(map), Map.values(map)}
sequence_of(values)
|> bind(&(Enum.zip(keys, &1) |> Enum.into(Map.new)))
end
end
iex(1)> "ab" |> parse(%{first: "a", last: "b"}))
{:ok, %{first: "a", last: "b"}}
element = [while(ASCII.upper, 1), while(ASCII.lower)]
!
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
!
reference = %{element: element, quantity: quantity}
!
formula = repeat(reference, at_least: 1)
!
parse("H2O", formula) |> IO.inspect
# {:ok, [%{element: ["H", ""], quantity: 2},
# %{element: ["O", ""], quantity: 1}]}
!
# Almost...
# parser join(p, joiner  ""),
# as: bind(p, &Enum.join(&1, joiner))
!
element = [while(ASCII.upper, 1), while(ASCII.lower)]
|> join
!
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
!
reference = %{element: element, quantity: quantity}
!
formula = repeat(reference, at_least: 1)
!
parse("H2O", formula) |> IO.inspect
# {:ok, [%{element: "H", quantity: 2},
# %{element: "O", quantity: 1}]}
!
# Yahoooo!!!
element = [while(ASCII.upper, 1), while(ASCII.lower)]
|> join
!
# Bub a `quantity` is a number greater than zero!
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
!
reference = %{element: element, quantity: quantity}
!
formula = repeat(reference, at_least: 1)
!
parse("Na0", formula) |> IO.inspect
# {:ok, [%{element: "Na", quantity: 0}]}
!
# Ouch...
# ...
# A `quantity` is a number greater than zero
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
|> only_if(&(&1 > 0))
!
# ...
!
parse("Na0", formula) |> IO.inspect
# {:error, "0 is not acceptable at 1:3"}
# ...
# A `quantity` is a number greater than zero
!
error_message = "quantity must be greather than 0 %AT%"
!
greater_than_zero = &{&1 > 0, error_message}
!
quantity = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
|> maybe(default: 1)
|> only_if(greater_than_zero))
!
# ...
!
parse("Na0", formula) |> IO.inspect
# {:error, "quantity must be greather than 0 at 1:3"}
# Parse something like `(1, (2, 3))`
!
number = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
!
# We need to name something that is not yet defined,
# actually we need to name something in its definition
!
list = one_of([number, ???])
|> separated_by(ASCII.comma)
|> surrounded_by(ASCII.round_brackets)
# In module Paco.Parser...
!
parser recursive(f) do
fn state, this ->
box(f.(this)).parse.(state, this)
end
end
# Parse something like `(1, (2, 3))`
!
number = while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
!
list = recursive(fn(list) ->
one_of([number, list])
|> separated_by(ASCII.comma)
|> surrounded_by(ASCII.round_brackets)
end)
!
parse("(1, 2)", list) |> IO.inspect
# {:ok, [1, 2]}
!
parse("(1, (2, 3))", list) |> IO.inspect
# {:ok, [1, [2, 3]]}
defmodule ListOfLists do
use Paco
alias Paco.ASCII
!
parser number do
while(ASCII.digit, at_least: 1)
|> bind(&String.to_integer/1)
end
!
parser list do
one_of([number, list])
|> separated_by(ASCII.comma)
|> surrounded_by(ASCII.round_brackets)
end
end
!
Paco.parse("1", ListOfLists.number) |> IO.inspect
# {:ok, 1}
# In module Paco...
!
defmacro __using__(_) do
quote do
import Paco.Macro.ParserModuleDefinition
import Paco.Parser
!
Module.register_attribute(__MODULE__,
:paco_parsers,
accumulate: true)
!
@before_compile Paco
end
end
# In module Paco...
!
defmacro __before_compile__(env) do
root_parser = pick_root_parser_between(
Module.get_attribute(env.module, :paco_parsers)
|> Enum.reverse
)
!
quote do
def parse(s, opts  []) do
Paco.parse(s, apply(__MODULE__, unquote(root_parser), []), opts)
end
!
def parse!(s, opts  []) do
Paco.parse!(s, apply(__MODULE__, unquote(root_parser), []), opts)
end
end
end
# Everything we saw until now works with streams of text!
!
["a", "b", "", "ab", "", "a", "", "", "b", "", ""]
|> Paco.Stream.parse(lit("ab"))
|> Enum.to_list
|> IO.inspect
# ["ab", "ab", "ab"]
!
[~s|{"foo|, ~s|": "bar"|, ~s|}[1, 2|, ~s|, 3]|]
|> Paco.Parser.JSON.stream
|> Enum.to_list
|> IO.inspect
# [%{"foo" => "bar"}, [1, 2, 3]]
parser lit(s) do
fn %State{at: from, text: text, stream: stream} = state, this ->
case Paco.String.consume(text, s, from) do
{tail, _, to, at} ->
%Success{from: from, to: to, at: at, tail: tail, result: s}
!
{:not_enough, _, _, _, _} when is_pid(stream) ->
wait_for_more_and_continue(state, this)

{_, _, _, _, {n, _, _}} ->
%Failure{at: from, tail: text, expected: s, rank: n+1}
end
end
end
!
defp wait_for_more_and_continue(state, this) do
%State{text: text, stream: stream} = state
send(stream, {self, :more})
receive do
{:load, more_text} ->
this.parse.(%State{state|text: text <> more_text}, this)
:halted ->
# The stream is over, switching to a non stream mode
# is the same as to tell the parser to behave knowing
# that more input will never come
this.parse.(%State{state|stream: nil}, this)
end
end
defmodule Paco.Parser.JSON do
alias Paco.ASCII
use Paco
!
root parser all, do: one_of([object, array])
!
parser object do
pair(string, value, separated_by: ASCII.colon)
|> separated_by(ASCII.comma)
|> surrounded_by(ASCII.curly_brackets)
|> bind(&to_map/1)
end
!
parser array do
value
|> separated_by(ASCII.comma)
|> surrounded_by(ASCII.square_brackets)
end
# ...
defmodule Paco.Parser.JSON do
# ...
!
parser value do
one_of([
string,
number,
object,
array,
literal_true,
literal_false,
literal_null])
end
!
parser string do
between(ASCII.double_quotes, escaped_with: "", strip: false)
|> bind(&replace_escapes_in_string/1)
end
# ...
defmodule Paco.Parser.JSON do
# ...
!
parser literal_true, do: lit("true") |> replace_with(true)
parser literal_false, do: lit("false") |> replace_with(false)
parser literal_null, do: lit("null") |> replace_with(nil)
!
# ...
end
Settings:
duration: 1.0 s
!
## Paco.Benchmark.JSON
[00:21:14] 1/4: poison small
[00:21:16] 2/4: poison medium
[00:21:18] 3/4: paco small
[00:21:21] 4/4: paco medium
!
Finished in 8.78 seconds
!
## Paco.Benchmark.JSON
poison small 100000 14.72 µs/op
poison medium 10000 144.58 µs/op
paco small 5000 493.32 µs/op
paco medium 500 4152.14 µs/op
✓
✓
☕
☕
👎
$
Parse complex formulas with Elixir's Paco parser library

More Related Content

What's hot

Erlang Introduction Bcberlin3
Erlang Introduction Bcberlin3Erlang Introduction Bcberlin3
Erlang Introduction Bcberlin3guesta3202
 
The Ring programming language version 1.6 book - Part 185 of 189
The Ring programming language version 1.6 book - Part 185 of 189The Ring programming language version 1.6 book - Part 185 of 189
The Ring programming language version 1.6 book - Part 185 of 189Mahmoud Samir Fayed
 
Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語ikdysfm
 
MySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelMySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelBlythe Dunham
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)riue
 
The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194Mahmoud Samir Fayed
 
What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)Kerry Buckley
 
Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to PythonUC San Diego
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to GroovyAnton Arhipov
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189Mahmoud Samir Fayed
 
Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)진성 오
 
Python tutorialfeb152012
Python tutorialfeb152012Python tutorialfeb152012
Python tutorialfeb152012Shani729
 
PHP and MySQL Tips and tricks, DC 2007
PHP and MySQL Tips and tricks, DC 2007PHP and MySQL Tips and tricks, DC 2007
PHP and MySQL Tips and tricks, DC 2007Damien Seguy
 
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲Mohammad Reza Kamalifard
 
Python tutorial
Python tutorialPython tutorial
Python tutorialRajiv Risi
 

What's hot (20)

Erlang Introduction Bcberlin3
Erlang Introduction Bcberlin3Erlang Introduction Bcberlin3
Erlang Introduction Bcberlin3
 
The Ring programming language version 1.6 book - Part 185 of 189
The Ring programming language version 1.6 book - Part 185 of 189The Ring programming language version 1.6 book - Part 185 of 189
The Ring programming language version 1.6 book - Part 185 of 189
 
Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語Haskellで学ぶ関数型言語
Haskellで学ぶ関数型言語
 
MySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next LevelMySQLConf2009: Taking ActiveRecord to the Next Level
MySQLConf2009: Taking ActiveRecord to the Next Level
 
関数潮流(Function Tendency)
関数潮流(Function Tendency)関数潮流(Function Tendency)
関数潮流(Function Tendency)
 
Elixir and OTP Apps introduction
Elixir and OTP Apps introductionElixir and OTP Apps introduction
Elixir and OTP Apps introduction
 
The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194The Ring programming language version 1.5.3 book - Part 19 of 194
The Ring programming language version 1.5.3 book - Part 19 of 194
 
PubNative Tracker
PubNative TrackerPubNative Tracker
PubNative Tracker
 
What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)What I learned from Seven Languages in Seven Weeks (IPRUG)
What I learned from Seven Languages in Seven Weeks (IPRUG)
 
Introduction to Python
Introduction to PythonIntroduction to Python
Introduction to Python
 
Introduction to Groovy
Introduction to GroovyIntroduction to Groovy
Introduction to Groovy
 
Elixir
ElixirElixir
Elixir
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 
Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)Swift에서 꼬리재귀 사용기 (Tail Recursion)
Swift에서 꼬리재귀 사용기 (Tail Recursion)
 
Python tutorialfeb152012
Python tutorialfeb152012Python tutorialfeb152012
Python tutorialfeb152012
 
PHP and MySQL Tips and tricks, DC 2007
PHP and MySQL Tips and tricks, DC 2007PHP and MySQL Tips and tricks, DC 2007
PHP and MySQL Tips and tricks, DC 2007
 
Pdr ppt
Pdr pptPdr ppt
Pdr ppt
 
Pure kotlin
Pure kotlinPure kotlin
Pure kotlin
 
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲
جلسه سوم پایتون برای هکر های قانونی دوره مقدماتی پاییز ۹۲
 
Python tutorial
Python tutorialPython tutorial
Python tutorial
 

Viewers also liked

Minimum Viable Product
Minimum Viable ProductMinimum Viable Product
Minimum Viable ProductGabriele Lana
 
Resource Oriented Design
Resource Oriented DesignResource Oriented Design
Resource Oriented DesignGabriele Lana
 
Professional Programmer
Professional ProgrammerProfessional Programmer
Professional ProgrammerGabriele Lana
 
Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Gabriele Lana
 
Agileday Coderetreat 2013
Agileday Coderetreat 2013Agileday Coderetreat 2013
Agileday Coderetreat 2013Gabriele Lana
 
Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Gabriele Lana
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to NodejsGabriele Lana
 
It is not supposed to fly but it does
It is not supposed to fly but it doesIt is not supposed to fly but it does
It is not supposed to fly but it doesGabriele Lana
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with ExamplesGabriele Lana
 

Viewers also liked (17)

API Over HTTP
API Over HTTPAPI Over HTTP
API Over HTTP
 
Minimum Viable Product
Minimum Viable ProductMinimum Viable Product
Minimum Viable Product
 
Why couchdb is cool
Why couchdb is coolWhy couchdb is cool
Why couchdb is cool
 
Magic of Ruby
Magic of RubyMagic of Ruby
Magic of Ruby
 
Nosql
NosqlNosql
Nosql
 
Resource Oriented Design
Resource Oriented DesignResource Oriented Design
Resource Oriented Design
 
MongoDB With Style
MongoDB With StyleMongoDB With Style
MongoDB With Style
 
Beyond Phoenix
Beyond PhoenixBeyond Phoenix
Beyond Phoenix
 
Professional Programmer
Professional ProgrammerProfessional Programmer
Professional Programmer
 
Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013Milano Legacy Coderetreat 2013
Milano Legacy Coderetreat 2013
 
Agileday Coderetreat 2013
Agileday Coderetreat 2013Agileday Coderetreat 2013
Agileday Coderetreat 2013
 
Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)Professional Programmer (3 Years Later)
Professional Programmer (3 Years Later)
 
coderetreat
coderetreatcoderetreat
coderetreat
 
CouchDB Vs MongoDB
CouchDB Vs MongoDBCouchDB Vs MongoDB
CouchDB Vs MongoDB
 
Introduction to Nodejs
Introduction to NodejsIntroduction to Nodejs
Introduction to Nodejs
 
It is not supposed to fly but it does
It is not supposed to fly but it doesIt is not supposed to fly but it does
It is not supposed to fly but it does
 
Nodejs Explained with Examples
Nodejs Explained with ExamplesNodejs Explained with Examples
Nodejs Explained with Examples
 

Similar to Parse complex formulas with Elixir's Paco parser library

Useful javascript
Useful javascriptUseful javascript
Useful javascriptLei Kang
 
jq: JSON - Like a Boss
jq: JSON - Like a Bossjq: JSON - Like a Boss
jq: JSON - Like a BossBob Tiernay
 
An overview of Python 2.7
An overview of Python 2.7An overview of Python 2.7
An overview of Python 2.7decoupled
 
app4.pptx
app4.pptxapp4.pptx
app4.pptxsg4795
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a bossgsterndale
 
Τα Πολύ Βασικά για την Python
Τα Πολύ Βασικά για την PythonΤα Πολύ Βασικά για την Python
Τα Πολύ Βασικά για την PythonMoses Boudourides
 
Are we ready to Go?
Are we ready to Go?Are we ready to Go?
Are we ready to Go?Adam Dudczak
 
The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210Mahmoud Samir Fayed
 
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"PromptWorks
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?Tomasz Wrobel
 
Beautiful python - PyLadies
Beautiful python - PyLadiesBeautiful python - PyLadies
Beautiful python - PyLadiesAlicia Pérez
 
Introduction to R programming
Introduction to R programmingIntroduction to R programming
Introduction to R programmingAlberto Labarga
 
Iterators, Hashes, and Arrays
Iterators, Hashes, and ArraysIterators, Hashes, and Arrays
Iterators, Hashes, and ArraysBlazing Cloud
 
The Ring programming language version 1.8 book - Part 29 of 202
The Ring programming language version 1.8 book - Part 29 of 202The Ring programming language version 1.8 book - Part 29 of 202
The Ring programming language version 1.8 book - Part 29 of 202Mahmoud Samir Fayed
 
Investigating Python Wats
Investigating Python WatsInvestigating Python Wats
Investigating Python WatsAmy Hanlon
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingSergey Shishkin
 

Similar to Parse complex formulas with Elixir's Paco parser library (20)

Combinator parsing
Combinator parsingCombinator parsing
Combinator parsing
 
Useful javascript
Useful javascriptUseful javascript
Useful javascript
 
jq: JSON - Like a Boss
jq: JSON - Like a Bossjq: JSON - Like a Boss
jq: JSON - Like a Boss
 
An overview of Python 2.7
An overview of Python 2.7An overview of Python 2.7
An overview of Python 2.7
 
A tour of Python
A tour of PythonA tour of Python
A tour of Python
 
app4.pptx
app4.pptxapp4.pptx
app4.pptx
 
Refactor like a boss
Refactor like a bossRefactor like a boss
Refactor like a boss
 
Term Rewriting
Term RewritingTerm Rewriting
Term Rewriting
 
Τα Πολύ Βασικά για την Python
Τα Πολύ Βασικά για την PythonΤα Πολύ Βασικά για την Python
Τα Πολύ Βασικά για την Python
 
Are we ready to Go?
Are we ready to Go?Are we ready to Go?
Are we ready to Go?
 
The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210The Ring programming language version 1.9 book - Part 31 of 210
The Ring programming language version 1.9 book - Part 31 of 210
 
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
 
An introduction to Ruby
An introduction to RubyAn introduction to Ruby
An introduction to Ruby
 
(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?(How) can we benefit from adopting scala?
(How) can we benefit from adopting scala?
 
Beautiful python - PyLadies
Beautiful python - PyLadiesBeautiful python - PyLadies
Beautiful python - PyLadies
 
Introduction to R programming
Introduction to R programmingIntroduction to R programming
Introduction to R programming
 
Iterators, Hashes, and Arrays
Iterators, Hashes, and ArraysIterators, Hashes, and Arrays
Iterators, Hashes, and Arrays
 
The Ring programming language version 1.8 book - Part 29 of 202
The Ring programming language version 1.8 book - Part 29 of 202The Ring programming language version 1.8 book - Part 29 of 202
The Ring programming language version 1.8 book - Part 29 of 202
 
Investigating Python Wats
Investigating Python WatsInvestigating Python Wats
Investigating Python Wats
 
Hitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional ProgrammingHitchhiker's Guide to Functional Programming
Hitchhiker's Guide to Functional Programming
 

More from Gabriele Lana

Microservice Architectures
Microservice ArchitecturesMicroservice Architectures
Microservice ArchitecturesGabriele Lana
 
Professional Programmer 2018
Professional Programmer 2018Professional Programmer 2018
Professional Programmer 2018Gabriele Lana
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartGabriele Lana
 
Erlang: the language and the platform
Erlang: the language and the platformErlang: the language and the platform
Erlang: the language and the platformGabriele Lana
 
Resource Oriented Architectures
Resource Oriented ArchitecturesResource Oriented Architectures
Resource Oriented ArchitecturesGabriele Lana
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile DevelopmentGabriele Lana
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to ErlangGabriele Lana
 

More from Gabriele Lana (8)

Microservice Architectures
Microservice ArchitecturesMicroservice Architectures
Microservice Architectures
 
Professional Programmer 2018
Professional Programmer 2018Professional Programmer 2018
Professional Programmer 2018
 
ProgrammingKatas
ProgrammingKatasProgrammingKatas
ProgrammingKatas
 
Refactoring In Tdd The Missing Part
Refactoring In Tdd The Missing PartRefactoring In Tdd The Missing Part
Refactoring In Tdd The Missing Part
 
Erlang: the language and the platform
Erlang: the language and the platformErlang: the language and the platform
Erlang: the language and the platform
 
Resource Oriented Architectures
Resource Oriented ArchitecturesResource Oriented Architectures
Resource Oriented Architectures
 
Sustainable Agile Development
Sustainable Agile DevelopmentSustainable Agile Development
Sustainable Agile Development
 
Introduction to Erlang
Introduction to ErlangIntroduction to Erlang
Introduction to Erlang
 

Recently uploaded

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesThousandEyes
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterMydbops
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesBernd Ruecker
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructureitnewsafrica
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxfnnc6jmgwh
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsRavi Sanghani
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesManik S Magar
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationKnoldus Inc.
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesKari Kakkonen
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Strongerpanagenda
 

Recently uploaded (20)

TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data PrivacyTrustArc Webinar - How to Build Consumer Trust Through Data Privacy
TrustArc Webinar - How to Build Consumer Trust Through Data Privacy
 
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyesHow to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
How to Effectively Monitor SD-WAN and SASE Environments with ThousandEyes
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Scale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL RouterScale your database traffic with Read & Write split using MySQL Router
Scale your database traffic with Read & Write split using MySQL Router
 
QCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architecturesQCon London: Mastering long-running processes in modern architectures
QCon London: Mastering long-running processes in modern architectures
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical InfrastructureVarsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
Varsha Sewlal- Cyber Attacks on Critical Critical Infrastructure
 
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptxGenerative AI - Gitex v1Generative AI - Gitex v1.pptx
Generative AI - Gitex v1Generative AI - Gitex v1.pptx
 
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
Potential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and InsightsPotential of AI (Generative AI) in Business: Learnings and Insights
Potential of AI (Generative AI) in Business: Learnings and Insights
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotesMuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
MuleSoft Online Meetup Group - B2B Crash Course: Release SparkNotes
 
Data governance with Unity Catalog Presentation
Data governance with Unity Catalog PresentationData governance with Unity Catalog Presentation
Data governance with Unity Catalog Presentation
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Testing tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examplesTesting tools and AI - ideas what to try with some tool examples
Testing tools and AI - ideas what to try with some tool examples
 
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better StrongerModern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
Modern Roaming for Notes and Nomad – Cheaper Faster Better Stronger
 

Parse complex formulas with Elixir's Paco parser library

  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10. Interactive Elixir (1.1.0) iex(1)> import Paco nil iex(2)> import Paco.Parser nil iex(3)> parse("a", lit("a")) {:ok, "a"} iex(4)> parse("a", lit("a"), format: :raw) %Paco.Success{ from: {0, 1, 1}, to: {0, 1, 1}, at: {1, 1, 2}, result: "a", tail: "", ...}
  • 11. iex(5)> parse("aaa", lit("aaa")) {:ok, "aaa"} iex(6)> "aaa" |> parse(lit("aaa")) {:ok, "aaa"} iex(7)> "aaa" |> parse(lit("a")) {:ok, "a"} iex(8)> "aaa" |> parse(lit("a"), format: :raw) %Paco.Success{ from: {0, 1, 1}, to: {0, 1, 1}, at: {1, 1, 2}, result: "a", tail: "aa", ...}
  • 12. iex(9)> "b" |> parse(lit("a")) {:error, "expected "a" at 1:1 but got "b""} iex(10)> "b" |> parse(lit("a"), format: :raw) %Paco.Failure{ at: {0, 1, 1}, expected: "a", tail: "b", ...}
  • 13. iex(1)> "aaa" |> parse(any) {:ok, "a"} iex(2)> "aaa" |> parse(any(1)) {:ok, "a"} iex(3)> "aaa" |> parse(any(2)) {:ok, "aa"} iex(4)> "a" |> parse(any(2)) {:error, "expected exactly 2 characters at 1:1 but got "a""} iex(5)> "aaa" |> parse(any(at_least: 2)) {:ok, "aaa"} iex(6)> "aaa" |> parse(any(at_most: 2)) {:ok, "aa"}
  • 14. iex(1)> "bbabcd" |> parse(while("abc")) {:ok, "bbabc"} iex(2)> "xxx" |> parse(while("abc")) {:ok, ""} iex(3)> "xxx" |> parse(while("abc", at_least: 2)) {:error, "expected at least 2 characters in alphabet "abc" at 1:1 but got "xx""} iex(4)> import Paco.ASCII, only: [lowercase?: 1] iex(5)> "abCD" |> parse(while(&lowercase?/1)) {:error, "ab"} iex(6)> "abCD" |> parse(while(&lowercase?/1, at_least: 3)) {:error, "expected at least 3 lowercase characters at 1:1 but got "abC""}
  • 15. iex(1)> "abc" |> parse(until("c")) {:ok, "ab"} iex(2)> "abcdc" |> parse(until("c", escaped_with: "")) {:ok, "abcd"} iex(3)> "abcdc" |> parse(until("c", escaped_with: "", keep_escape: true)) {:ok, "abcd"} iex(4)> "abc" |> parse(until("d")) {:error, "expected something ended by "d" at 1:1 but got "abc""} iex(5)> "abc" |> parse(until("d"), eof: true) {:error, "abc"}
  • 16. iex(1)> "ab" |> parse(sequence_of([lit("a"), lit("b")])) {:ok, ["a", "b"]} iex(2)> "ac" |> parse(sequence_of([lit("a"), lit("b")])) {:error, "expected "b" at 1:2 but got "c""}} iex(3)> ab = sequence_of([lit("a"), lit("b")]) %Paco.Parser{...} iex(4)> "abc" |> parse(sequence_of([ab, lit("c")])) {:ok, [["a", "b"], "c"]} iex(5)> "xxx" |> parse(sequence_of([ab, lit("c")])) {:error, "expected "a" at 1:1 but got "x""} iex(6)> "axx" |> parse(sequence_of([ab, lit("c")])) {:error, "expected "b" at 1:2 but got "x""} iex(7)> "abx" |> parse(sequence_of([ab, lit("c")])) {:error, "expected "c" at 1:3 but got "x""}
  • 17. iex(1)> "a" |> parse(one_of([lit("a"), lit("b")])) {:ok, "a"} iex(2)> "b" |> parse(one_of([lit("a"), lit("b")])) {:ok, "b"} iex(4)> # farthest failure (higher rank) wins nil iex(3)> "ab" |> parse(one_of([lit("ac"), lit("bc")])) {:error, "expected "ac" at 1:1 but got "ab""} iex(6)> # failures with same rank are composed nil iex(5)> "ab" |> parse(one_of([lit("ac"), lit("ad")])) {:error, "expected one of ["ac", "ad"] at 1:1 but got "ab""}
  • 18. iex(1)> "aaa" |> parse(repeat(lit("a"))) {:ok, ["a", "a", "a"]} iex(2)> "aaa" |> parse(repeat(lit("a"), 2)) {:ok, ["a", "a"]} iex(4)> "aaa" |> parse(repeat(lit("a"), at_most: 2)) {:ok, ["a", "a"]} iex(3)> "aaa" |> parse(repeat(lit("a"), at_least: 4)) {:error, ""expected "a" at 1:2 but got the end of input"} iex(6)> "abba" |> parse(repeat(one_of([lit("a"), lit("b")]))) {:ok, ["a", "b", "b", "a"]}
  • 19. defmodule Paco.ASCII do ! @upper ["A","B","C","D","E",...,"Z"] ! @classes [... {:upper, :upper?, @upper}, ...] for {class, is_class, elements} <- @classes do def unquote(class)(), do: unquote(elements) for element <- elements do def unquote(is_class)(<<unquote(element)>>), do: true end def unquote(is_class)(_), do: false end ! # def upper, do: @upper # def upper?("A"), do: true # def upper?("B"), do: true # ... # def upper?(_), do: false
  • 20. ws = while(ASCII.ws) ! hello = lit("Hello") separator = sequence_of([ws, lit(","), ws]) what = while(ASCII.letter, at_least: 1) terminator = sequence_of([ws, lit("!")]) ! greetings = sequence_of([hello, separator, what, terminator]) ! parse("Hello,World!", greetings) |> IO.inspect # {:ok, ["Hello", ["", ",", ""], "World", ["", "!"]]} ! parse("Hello, BEAM!", greetings) |> IO.inspect # {:ok, ["Hello", ["", ",", " "], "BEAM", ["", "!"]]}
  • 21. # Good, not great: skip everything that is not interesting ! ws = while(ASCII.ws) ! hello = lit("Hello") |> skip separator = sequence_of([ws, lit(","), ws]) |> skip what = while(ASCII.letter, at_least: 1) terminator = sequence_of([ws, lit("!")]) |> skip ! greetings = sequence_of([hello, separator, what, terminator]) ! parse("Hello,World!", greetings) |> IO.inspect # {:ok, ["World"]} ! parse("Hello, BEAM!", greetings) |> IO.inspect # {:ok, ["BEAM"]}
  • 22. # Not everyone are so loud, `!` should be optional ! ws = while(ASCII.ws) ! hello = lit("Hello") |> skip separator = sequence_of([ws, lit(","), ws]) |> skip what = while(ASCII.letter, at_least: 1) terminator = sequence_of([ws, lit("!")]) |> maybe ! greetings = sequence_of([hello, separator, what, terminator]) ! parse("Hello, BEAM!", greetings) |> IO.inspect # {:ok, ["BEAM"]} ! parse("Hello, BEAM", greetings) |> IO.inspect # {:ok, ["BEAM"]}
  • 23. # Let's get rid of non significant whitespaces with lex(s) ! # In module Paco.Parser... ! parser lex(s), as: lit(s) |> surrounded_by(maybe(whitespaces)) ! parser surrounded_by(p, left, right), as: sequence_of([skip(left), p, skip(right)])
  • 24. # Use lex Luke! ! ws = while(ASCII.ws) ! hello = lit("Hello") |> skip what = while(ASCII.letter, at_least: 1) separator = lex(",") |> skip terminator = lex("!") |> maybe ! greetings = sequence_of([hello, separator, what, terminator]) ! parse("Hello, BEAM!", greetings) |> IO.inspect # {:ok, ["BEAM"]} ! parse("Hello, BEAM", greetings) |> IO.inspect # {:ok, ["BEAM"]}
  • 25. # It's common to have something non significant # that follows or precedes something significant ! # In module Paco.Parser... ! parser followed_by(p, right), as: sequence_of([p, skip(right)]) ! parser preceded_by(p, right), as: sequence_of([skip(left), p])
  • 26. # An alternative and shorter version ! what = while(ASCII.letter, at_least: 1) ! greetings = what |> preceded_by(lit("Hello") |> followed_by(lex(","))) |> followed_by(maybe(lex("!"))) ! ! parse("Hello, BEAM!", greetings) |> IO.inspect # {:ok, ["BEAM"]} ! parse("Hello, BEAM", greetings) |> IO.inspect # {:ok, ["BEAM"]}
  • 27. # Parse a sequence of numbers separated by `+` or `-` ! number = while(ASCII.digit, at_least: 1) operator = one_of([lex("+"), lex("-")]) ! expression = number |> separated_by(operator) ! parse("1", expression) |> IO.inspect # {:ok, ["1"]} ! parse("1 + 2", expression) |> IO.inspect # {:ok, ["1", "2"]} ! parse("1 + 2 - 3", expression) |> IO.inspect # {:ok, ["1", "2", "3"]} ! # Small problem... to compute the value we need the operators!
  • 28. # Parse a sequence of numbers separated by `+` or `-` ! number = while(ASCII.digit, at_least: 1) operator = one_of([lex("+"), lex("-")]) ! expression = number |> separated_by(keep(operator)) ! parse("1", expression) |> IO.inspect # {:ok, ["1"]} ! parse("1 + 2", expression) |> IO.inspect # {:ok, ["1", "+", "2"]} ! parse("1 + 2 - 3", expression) |> IO.inspect # {:ok, ["1", "+", "2", "-", "3"]} ! # Ok, but we need numbers not strings
  • 29. # In module Paco.Parser... parser bind(p, f) do fn state, _ -> case p.parse.(state, p) do %Success{result: result} = success -> case f.(result, success) do %Failure{} = failure -> failure %Success{} = success -> success result -> %Success{success|result: result} end %Failure{} = failure -> failure end end end
  • 30. # Parse a sequence of numbers separated by `+` or `-` ! number = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) ! operator = one_of([lex("+"), lex("-")]) ! expression = number |> separated_by(keep(operator)) ! parse("1 + 2 - 3", expression) |> IO.inspect # {:ok, [1, "+", 2, "-", 3]} ! # Missing only the last step... compute the result :-)
  • 31. # Parse a sequence of numbers separated by `+` or `-` ! number = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) ! operator = one_of([lex("+"), lex("-")]) ! expression = number |> separated_by(keep(operator)) |> bind(&Paco.Transform.separated_by(&1, fn("+", n, m) -> n + m ("-", n, m) -> n - m end)) ! ! parse("1 + 2 - 3", expression) |> IO.inspect # {:ok, 0]}
  • 32. # Parse a{n}b{n}c{n} where n ∈ ℕ ! # If you knew the `n` (ex. 3) it would be easy ! p = sequence_of([while("a", 3), while("b", 3), while("c", 3)]) ! parse("aaabbbccc", p) |> IO.inspect # {:ok, ["aaa", "bbb", "ccc"]} ! # We need to be able to peek ahead and then create a parser # with that knowledge
  • 33. # In module Paco.Parser... ! parser peek(box(p)) do fn %State{at: at, text: text} = state, _ -> case p.parse.(state, p) do %Success{result: result} -> %Success{from: at, to: at, at: at, tail: text, result: result} %Failure{} = failure -> failure end end end
  • 34. # In module Paco.Parser... ! parser then(p, f) when is_function(f), as: bind(p, f) |> bind(fn(p, _, s) -> p.parse.(s, p) end)
  • 35. # Parse a{n}b{n}c{n} where n ∈ ℕ ! p = peek(while("a")) |> then(fn(a) -> len = String.length(a) sequence_of([while("a", len), while("b", len), while("c", len)]) end) ! parse("aaabbbccc", p) |> IO.inspect # {:ok, ["aaa", "bbb", "ccc"]} ! parse("aaabbccc", p) |> IO.inspect # {:error, "expected exactly 3 characters in alphabet "b" at 1:4 but got "bbc""}
  • 36. # An `element` is a word beginning with one uppercase letter # followed by zero or more lowercase letters element = sequence_of([while(ASCII.upper, 1), while(ASCII.lower)]) ! # A `quantity` is a number greater than zero # If the quantity is omitted assume the value of 1 as default quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) ! # A `reference` is an element optionally followed by a quantity reference = sequence_of([element, quantity]) ! formula = repeat(reference, at_least: 1)
  • 37. parse("H2O", formula) |> IO.inspect # {:ok, [[["H", ""], 2], [["O", ""], 1]]} ! # That's right but the output format sucks! ! # What we really want is something like # {:ok, [%{element: "H", quantity: 2}, %{element: "0", quantity: 1}] ! # Is that possible???
  • 38. defprotocol Paco.Parsable do @moduledoc """ A protocol that converts terms into Paco parsers """ @fallback_to_any true @doc """ Returns a parser that parses `t` and keeps the shape of `t` """ @spec to_parser(t) :: Paco.Parser.t def to_parser(t) end
  • 39. defimpl Paco.Parsable, for: BitString do import Paco.Parser def to_parser(s) when is_binary(s) do lit(s) end def to_parser(s) do raise Protocol.UndefinedError, protocol: @protocol, value: s end end iex(1)> "aaa" |> parse(lit("aaa")) {:ok, "aaa"} iex(2)> "aaa" |> parse("aaa") {:ok, "aaa"}
  • 40. defimpl Paco.Parsable, for: List do import Paco.Parser def to_parser(l) do sequence_of(l) end end iex(1)> "ab" |> parse(sequence_of([lit("a"), lit("b")])) {:ok, ["a", "b"]} iex(2)> "ab" |> parse(sequence_of(["a", "b"])) {:ok, ["a", "b"]} iex(3)> "ab" |> parse(["a", "b"]) {:ok, ["a", "b"]}
  • 41. defimpl Paco.Parsable, for: Tuple do import Paco.Parser def to_parser(tuple) do sequence_of(Tuple.to_list(tuple)) |> bind(&List.to_tuple/1) end end iex(1)> "ab" |> parse({"a", "b"})) {:ok, {"a", "b"}}
  • 42. defimpl Paco.Parsable, for: Map do import Paco.Parser def to_parser(tuple) do {keys, values} = {Map.keys(map), Map.values(map)} sequence_of(values) |> bind(&(Enum.zip(keys, &1) |> Enum.into(Map.new))) end end iex(1)> "ab" |> parse(%{first: "a", last: "b"})) {:ok, %{first: "a", last: "b"}}
  • 43. element = [while(ASCII.upper, 1), while(ASCII.lower)] ! quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) ! reference = %{element: element, quantity: quantity} ! formula = repeat(reference, at_least: 1) ! parse("H2O", formula) |> IO.inspect # {:ok, [%{element: ["H", ""], quantity: 2}, # %{element: ["O", ""], quantity: 1}]} ! # Almost...
  • 44. # parser join(p, joiner ""), # as: bind(p, &Enum.join(&1, joiner)) ! element = [while(ASCII.upper, 1), while(ASCII.lower)] |> join ! quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) ! reference = %{element: element, quantity: quantity} ! formula = repeat(reference, at_least: 1) ! parse("H2O", formula) |> IO.inspect # {:ok, [%{element: "H", quantity: 2}, # %{element: "O", quantity: 1}]} ! # Yahoooo!!!
  • 45. element = [while(ASCII.upper, 1), while(ASCII.lower)] |> join ! # Bub a `quantity` is a number greater than zero! quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) ! reference = %{element: element, quantity: quantity} ! formula = repeat(reference, at_least: 1) ! parse("Na0", formula) |> IO.inspect # {:ok, [%{element: "Na", quantity: 0}]} ! # Ouch...
  • 46. # ... # A `quantity` is a number greater than zero quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) |> only_if(&(&1 > 0)) ! # ... ! parse("Na0", formula) |> IO.inspect # {:error, "0 is not acceptable at 1:3"}
  • 47. # ... # A `quantity` is a number greater than zero ! error_message = "quantity must be greather than 0 %AT%" ! greater_than_zero = &{&1 > 0, error_message} ! quantity = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) |> maybe(default: 1) |> only_if(greater_than_zero)) ! # ... ! parse("Na0", formula) |> IO.inspect # {:error, "quantity must be greather than 0 at 1:3"}
  • 48. # Parse something like `(1, (2, 3))` ! number = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) ! # We need to name something that is not yet defined, # actually we need to name something in its definition ! list = one_of([number, ???]) |> separated_by(ASCII.comma) |> surrounded_by(ASCII.round_brackets)
  • 49. # In module Paco.Parser... ! parser recursive(f) do fn state, this -> box(f.(this)).parse.(state, this) end end
  • 50. # Parse something like `(1, (2, 3))` ! number = while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) ! list = recursive(fn(list) -> one_of([number, list]) |> separated_by(ASCII.comma) |> surrounded_by(ASCII.round_brackets) end) ! parse("(1, 2)", list) |> IO.inspect # {:ok, [1, 2]} ! parse("(1, (2, 3))", list) |> IO.inspect # {:ok, [1, [2, 3]]}
  • 51. defmodule ListOfLists do use Paco alias Paco.ASCII ! parser number do while(ASCII.digit, at_least: 1) |> bind(&String.to_integer/1) end ! parser list do one_of([number, list]) |> separated_by(ASCII.comma) |> surrounded_by(ASCII.round_brackets) end end ! Paco.parse("1", ListOfLists.number) |> IO.inspect # {:ok, 1}
  • 52. # In module Paco... ! defmacro __using__(_) do quote do import Paco.Macro.ParserModuleDefinition import Paco.Parser ! Module.register_attribute(__MODULE__, :paco_parsers, accumulate: true) ! @before_compile Paco end end
  • 53. # In module Paco... ! defmacro __before_compile__(env) do root_parser = pick_root_parser_between( Module.get_attribute(env.module, :paco_parsers) |> Enum.reverse ) ! quote do def parse(s, opts []) do Paco.parse(s, apply(__MODULE__, unquote(root_parser), []), opts) end ! def parse!(s, opts []) do Paco.parse!(s, apply(__MODULE__, unquote(root_parser), []), opts) end end end
  • 54. # Everything we saw until now works with streams of text! ! ["a", "b", "", "ab", "", "a", "", "", "b", "", ""] |> Paco.Stream.parse(lit("ab")) |> Enum.to_list |> IO.inspect # ["ab", "ab", "ab"] ! [~s|{"foo|, ~s|": "bar"|, ~s|}[1, 2|, ~s|, 3]|] |> Paco.Parser.JSON.stream |> Enum.to_list |> IO.inspect # [%{"foo" => "bar"}, [1, 2, 3]]
  • 55. parser lit(s) do fn %State{at: from, text: text, stream: stream} = state, this -> case Paco.String.consume(text, s, from) do {tail, _, to, at} -> %Success{from: from, to: to, at: at, tail: tail, result: s} ! {:not_enough, _, _, _, _} when is_pid(stream) -> wait_for_more_and_continue(state, this)
 {_, _, _, _, {n, _, _}} -> %Failure{at: from, tail: text, expected: s, rank: n+1} end end end !
  • 56. defp wait_for_more_and_continue(state, this) do %State{text: text, stream: stream} = state send(stream, {self, :more}) receive do {:load, more_text} -> this.parse.(%State{state|text: text <> more_text}, this) :halted -> # The stream is over, switching to a non stream mode # is the same as to tell the parser to behave knowing # that more input will never come this.parse.(%State{state|stream: nil}, this) end end
  • 57.
  • 58. defmodule Paco.Parser.JSON do alias Paco.ASCII use Paco ! root parser all, do: one_of([object, array]) ! parser object do pair(string, value, separated_by: ASCII.colon) |> separated_by(ASCII.comma) |> surrounded_by(ASCII.curly_brackets) |> bind(&to_map/1) end ! parser array do value |> separated_by(ASCII.comma) |> surrounded_by(ASCII.square_brackets) end # ...
  • 59. defmodule Paco.Parser.JSON do # ... ! parser value do one_of([ string, number, object, array, literal_true, literal_false, literal_null]) end ! parser string do between(ASCII.double_quotes, escaped_with: "", strip: false) |> bind(&replace_escapes_in_string/1) end # ...
  • 60. defmodule Paco.Parser.JSON do # ... ! parser literal_true, do: lit("true") |> replace_with(true) parser literal_false, do: lit("false") |> replace_with(false) parser literal_null, do: lit("null") |> replace_with(nil) ! # ... end
  • 61. Settings: duration: 1.0 s ! ## Paco.Benchmark.JSON [00:21:14] 1/4: poison small [00:21:16] 2/4: poison medium [00:21:18] 3/4: paco small [00:21:21] 4/4: paco medium ! Finished in 8.78 seconds ! ## Paco.Benchmark.JSON poison small 100000 14.72 µs/op poison medium 10000 144.58 µs/op paco small 5000 493.32 µs/op paco medium 500 4152.14 µs/op