SlideShare una empresa de Scribd logo
1 de 63
Descargar para leer sin conexión
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

Más contenido relacionado

La actualidad más candente

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
 

La actualidad más candente (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
 

Destacado

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
 

Destacado (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 a 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 a 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
 

Más de 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
 

Más de 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
 

Último

Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLScyllaDB
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxNavinnSomaal
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piececharlottematthew16
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 

Último (20)

Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Developer Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQLDeveloper Data Modeling Mistakes: From Postgres to NoSQL
Developer Data Modeling Mistakes: From Postgres to NoSQL
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 
SAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptxSAP Build Work Zone - Overview L2-L3.pptx
SAP Build Work Zone - Overview L2-L3.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Story boards and shot lists for my a level piece
Story boards and shot lists for my a level pieceStory boards and shot lists for my a level piece
Story boards and shot lists for my a level piece
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 

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