Skip to content

Commit

Permalink
Adds some bitwise functions to show jenkins hash
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucassifoni committed Apr 7, 2024
1 parent ac1f2de commit 7e66892
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 12 deletions.
2 changes: 1 addition & 1 deletion ovo/lib/ovo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,5 @@ defmodule Ovo do
iex> {%Ovo.Ast{kind: :integer, nodes: [], value: 3}}
"""
def run(code, input \\ %{}, log \\ false), do: Ovo.Interpreter.run(code, input, log)
def run(code, input \\ %{}), do: Ovo.Interpreter.run(code, input)
end
85 changes: 84 additions & 1 deletion ovo/lib/ovo/builtins.ex
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,93 @@ defmodule Ovo.Builtins do
"lesser_or_equals" => &lesser_or_equals(&1, &2),
"strictly_greater" => &strictly_greater(&1, &2),
"strictly_smaller" => &strictly_smaller(&1, &2),
"different" => &different(&1, &2)
"different" => &different(&1, &2),
"length" => &o_len(&1, &2),
"intval" => &intval(&1, &2),
"at" => &at(&1, &2),
"lshift" => &lshift(&1, &2),
"rshift" => &rshift(&1, &2),
"xor" => &xor(&1, &2),
"overflow" => &overflow(&1, &2)
}
end

defp overflow(nodes, env) do
import Bitwise

case map_nodes(nodes, env) do
[%{kind: :integer, value: v}] ->
Ovo.Ast.integer(v &&& 0xFFFFFFFF)

_ ->
:error
end
end

defp lshift(nodes, env) do
import Bitwise

case map_nodes(nodes, env) do
[%{kind: :integer, value: v}, %{kind: :integer, value: v1}] ->
Ovo.Ast.integer(v <<< v1)

_ ->
:error
end
end

defp rshift(nodes, env) do
import Bitwise

case map_nodes(nodes, env) do
[%{kind: :integer, value: v}, %{kind: :integer, value: v1}] ->
Ovo.Ast.integer(v >>> v1)

_ ->
:error
end
end

defp xor(nodes, env) do
import Bitwise

case map_nodes(nodes, env) do
[%{kind: :integer, value: v}, %{kind: :integer, value: v1}] ->
Ovo.Ast.integer(bxor(v, v1))

_ ->
:error
end
end

defp intval(nodes, env) do
case map_nodes(nodes, env) do
[%{kind: :string, value: v}] ->
<<k::utf8>> = v
Ovo.Ast.integer(k)

_ ->
:error
end
end

defp o_len(nodes, env) do
case map_nodes(nodes, env) do
[%{kind: :string, value: v}] -> Ovo.Ast.integer(String.length(v))
_ -> :error
end
end

defp at(nodes, env) do
case map_nodes(nodes, env) do
[%{kind: :string, value: v}, %{kind: :integer, value: v1}] ->
Ovo.Ast.string(String.at(v, v1))

_ ->
:error
end
end

defp concat(nodes, env) do
case map_nodes(nodes, env) do
[%{kind: :string, value: v}, %{kind: :string, value: v2}] ->
Expand Down
9 changes: 9 additions & 0 deletions ovo/lib/ovo/infix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ defmodule Ovo.Infix do

def token_to_text(token) do
case token do
:xor -> "^"
:lshift -> "<<"
:rshift -> ">>"
:different -> "!="
:identical -> "=="
:lt -> "<="
Expand All @@ -23,6 +26,9 @@ defmodule Ovo.Infix do
">=" -> :gt
">" -> :strict_gt
"<" -> :strict_lt
"^" -> :xor
">>" -> :rshift
"<<" -> :lshift
end
end

Expand All @@ -34,6 +40,9 @@ defmodule Ovo.Infix do
:gt -> "greater_or_equals"
:strict_lt -> "strictly_smaller"
:strict_gt -> "strictly_greater"
:xor -> "xor"
:lshift -> "lshift"
:rshift -> "rshift"
end
end
end
3 changes: 1 addition & 2 deletions ovo/lib/ovo/interpreter.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,10 @@ defmodule Ovo.Interpreter do
def run(ast), do: run(ast, %{})

@spec run(binary() | Ast.t(), map()) :: {Ovo.Ast.t(), map()}
def run(code, input, log) when is_binary(code) do
def run(code, input) when is_binary(code) do
tokens = Ovo.Tokenizer.tokenize(code)
{:ok, ast, _} = Ovo.Parser.parse(tokens)
rewritten = Ovo.Rewrites.rewrite(ast)
log && IO.inspect(rewritten)
run(rewritten, input)
end

Expand Down
16 changes: 13 additions & 3 deletions ovo/lib/ovo/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,19 @@ defmodule Ovo.Parser do

def p_infixer(tokens) do
case C.all([
C.any([&p_symbol/1, &p_parenthesized_expression/1]),
C.any([C.take(:different), C.take(:identical), C.take(:lt), C.take(:gt)]),
C.any([&p_symbol/1, &p_parenthesized_expression/1])
C.any([&p_symbol/1, &p_parenthesized_expression/1, &p_value/1]),
C.any([
C.take(:different),
C.take(:identical),
C.take(:lt),
C.take(:gt),
C.take(:strict_gt),
C.take(:strict_lt),
C.take(:xor),
C.take(:lshift),
C.take(:rshift)
]),
C.any([&p_symbol/1, &p_parenthesized_expression/1, &p_value/1])
]).(tokens) do
{:ok, [e1, infix, e2], rest} -> {:ok, Ast.infix(infix, [e1, e2]), rest}
b -> b
Expand Down
2 changes: 1 addition & 1 deletion ovo/lib/ovo/rewrites.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Ovo.Rewrites do
%Ast{
kind: :call,
value: %Ovo.Ast{kind: :symbol, nodes: [], value: Ovo.Infix.infix_to_builtin(op)},
nodes: [left, right]
nodes: [rw(left), rw(right)]
}
end

Expand Down
11 changes: 7 additions & 4 deletions ovo/lib/ovo/tokenizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ defmodule Ovo.Tokenizer do

def walk(pat("`"), s, o, b), do: acc(tail, s, o, b, :string)
def walk(pat("->"), s, o, b), do: acc(tail, s, o, b, :arrow, nil)
def walk(pat("^"), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token("^"), nil)
def walk(pat(">>"), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token(">>"), nil)
def walk(pat("<<"), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token("<<"), nil)
def walk(pat("<="), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token("<="), nil)
def walk(pat(">="), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token(">="), nil)
def walk(pat(">"), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token(">"), nil)
Expand All @@ -127,10 +130,10 @@ defmodule Ovo.Tokenizer do
def walk(pat("!="), s, o, b), do: acc(tail, s, o, b, Ovo.Infix.text_to_token("!="), nil)
def walk(pat("="), s, o, b), do: acc(tail, s, o, b, :equals, nil)
def walk(pat("!"), s, o, b), do: acc(tail, s, o, b, :shake, nil)
def walk(pat("if"), s, o, b), do: acc(tail, s, o, b, :if, nil)
def walk(pat("else"), s, o, b), do: acc(tail, s, o, b, :else, nil)
def walk(pat("then"), s, o, b), do: acc(tail, s, o, b, :then, nil)
def walk(pat("end"), s, o, b), do: acc(tail, s, o, b, :end, nil)
def walk(pat("if"), s, o, b) when s != :symbol, do: acc(tail, s, o, b, :if, nil)
def walk(pat("else"), s, o, b) when s != :symbol, do: acc(tail, s, o, b, :else, nil)
def walk(pat("then"), s, o, b) when s != :symbol, do: acc(tail, s, o, b, :then, nil)
def walk(pat("end"), s, o, b) when s != :symbol, do: acc(tail, s, o, b, :end, nil)
def walk(pat(","), s, o, b), do: acc(tail, s, o, b, :comma, nil)
def walk(pat("("), s, o, b), do: acc(tail, s, o, b, :open_paren, nil)

Expand Down
26 changes: 26 additions & 0 deletions ovo/test/ovo_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -436,4 +436,30 @@ defmodule OvoTest do
{:ok, parsed, _} = parse(input)
assert Ovo.Printer.print(Ovo.Rewrites.rewrite(parsed)) != Ovo.Printer.print(parsed)
end

test "jenkins hash" do
# https://en.wikipedia.org/wiki/Jenkins_hash_function
# one_at_a_time("The quick brown fox jumps over the lazy dog", 43)
# 0x519e91f5
input = """
len = length(data)
cycle = \\h, i ->
if i == len then
h
else
outa = overflow(add(h, intval(at(data, i))))
outb = overflow(add(outa, overflow(outa << 10)))
outc = overflow(outb ^ (overflow((outb >> 6))))
overflow(cycle(outc, add(i, 1)))
end
end
hash = overflow(cycle(0, 0))
hash = overflow(add(hash, overflow(hash << 3)))
hash = overflow(hash ^ (overflow(hash >> 11)))
hash = overflow(add(hash, overflow(hash << 15)))
"""

assert {%Ovo.Ast{kind: :integer, nodes: [], value: 0x519E91F5}, _} =
Ovo.run(input, "The quick brown fox jumps over the lazy dog")
end
end

0 comments on commit 7e66892

Please sign in to comment.