Skip to content

Commit

Permalink
add protodef spec tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hansihe committed Mar 27, 2016
1 parent b575440 commit a780858
Show file tree
Hide file tree
Showing 22 changed files with 408 additions and 55 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "priv/protodef-spec"]
path = priv/protodef-spec
url = https://github.com/rom1504/ProtoDef.git
2 changes: 2 additions & 0 deletions lib/ast_utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ defmodule ProtoDef.AstUtils do
|> Enum.concat
end

def extract_block({:__block__, _, [expr]}), do: expr

end
12 changes: 9 additions & 3 deletions lib/compiler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ defmodule ProtoDef.Compiler do
end

defmodule Context do
defstruct native_types: %{}, params: %{}, types: %{}
defstruct native_types: %{}, params: %{}, types: %{},
parse_name: &ProtoDef.Util.camel_string_to_snake_atom/1


@type t :: %__MODULE__{}

Expand All @@ -30,9 +32,9 @@ defmodule ProtoDef.Compiler do
ctx.types[type_name]
end

def type_add(ctx, type_name, type_kind, definition = %CustomType{}) when type_kind in [:inline_json] do
def type_add(ctx, type_name, definition) do
%{ ctx |
types: Map.put(ctx.types, type_name, {type_kind, definition}),
types: Map.put(ctx.types, type_name, definition),
}
end
end
Expand All @@ -47,12 +49,16 @@ defmodule ProtoDef.Compiler do
# Resolve field references
descr = ProtoDef.Compiler.Resolve.run(descr, [], ctx)

# Generate structure
structure = ProtoDef.Compiler.Structure.gen_for_type(descr, ctx)

# Generate the Elixir AST
decoder_ast = ProtoDef.Compiler.GenAst.decoder(descr, ctx)
encoder_ast = ProtoDef.Compiler.GenAst.encoder(descr, ctx)

%{
descr: descr,
structure: structure,
decoder_ast: decoder_ast,
encoder_ast: encoder_ast,
}
Expand Down
4 changes: 2 additions & 2 deletions lib/compiler/count.ex
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ defmodule ProtoDef.Compiler.Count do

def decoder_ast(%__MODULE__{kind: :fixed} = cnt, ctx) do
quote do
{unquote(cnt.value), data}
{unquote(cnt.value), unquote(ProtoDef.Type.data_var)}
end
end
def decoder_ast(%__MODULE__{kind: :field} = cnt, ctx) do
Expand Down Expand Up @@ -91,7 +91,7 @@ defmodule ProtoDef.Compiler.Count do
encoder = ProtoDef.Compiler.GenAst.encoder(cnt.value, ctx)
quote do
with do
unquote(@input_var) = unquote(count_var)
unquote(ProtoDef.Type.input_var) = unquote(count_var)
unquote(encoder)
end
end
Expand Down
5 changes: 1 addition & 4 deletions lib/compiler/preprocess.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ defmodule ProtoDef.Compiler.Preprocess do
if type_mod do
apply(type_mod, :preprocess, [args, ctx])
else
%ProtoDef.Type.TypeRef{
type_id: type_id,
args: args,
}
ProtoDef.Type.TypeRef.preprocess_typeref(type_id, args, ctx)
end
end

Expand Down
Empty file removed lib/mc_types.ex
Empty file.
2 changes: 1 addition & 1 deletion lib/type/array.ex
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ defmodule ProtoDef.Type.Array do
count_var = Macro.var(:count, ProtoDef.Type.Array)
count_encoder = ProtoDef.Compiler.Count.encoder_ast(descr.count, count_var, ctx)
item_encoder = ProtoDef.Compiler.GenAst.encoder(descr.type, ctx)
ret = quote do
quote do
with do
unquote(count_var) = Enum.count(unquote(@input_var))
count_head = unquote(count_encoder)
Expand Down
74 changes: 66 additions & 8 deletions lib/type/bitfield.ex
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
defmodule ProtoDef.Type.BitField do
@behaviour ProtoDef.Type
use ProtoDef.Type

@field_keys_message "Each field in bitfield must contain name, size and signed"
@field_size_total_message "The sum of all fields in a bitfield must be dividable by 8"

defstruct fields: []
defstruct fields: [], pad: nil, ident: nil

defmodule Field do
defstruct name: nil, size: nil, signed: nil
end

# Preprocess pass

def preprocess(args, ctx) do
fields = Enum.map(args, &(preprocess_field(&1, ctx)))

if !rem(Enum.reduce(fields, 0, &(&1 + &2)), 8) do
raise ProtoDef.CompileError, message: @field_size_total_message
end
pad = rem(Enum.reduce(fields, 0, &(&1.size + &2)), 8)

%__MODULE__{
fields: fields,
pad: pad,
}
end

Expand All @@ -27,12 +28,12 @@ defmodule ProtoDef.Type.BitField do
size = field["size"]
signed = field["signed"]

preprocess_field_make(name, size, signed)
preprocess_field_make(name, size, signed, ctx)
end

def preprocess_field_make(name, size, signed) when is_binary(name) and is_integer(size) and size > 0 and is_boolean(signed) do
def preprocess_field_make(name, size, signed, ctx) when is_binary(name) and is_integer(size) and size > 0 and is_boolean(signed) do
%Field{
name: name,
name: ctx.parse_name.(name),
size: size,
signed: signed,
}
Expand All @@ -41,4 +42,61 @@ defmodule ProtoDef.Type.BitField do
raise ProtoDef.CompileError, message: @field_keys_message
end

# Struct pass

def structure(type, _ctx) do
{:bitfield, Enum.map(type.fields, &(&1.name))}
end

# Assign pass

def assign_vars(descr, num, ctx) do
{ident, num} = ProtoDef.Compiler.AssignIdents.make_ident(num, ctx)
descr = %{ descr |
ident: ident,
}
{descr, ident, num}
end

# Decoder AST pass

def decoder_ast(descr, ctx) do
field_decoders = Enum.map(descr.fields, &item_decoder_ast(&1, ctx))
field_assigners = Enum.map(descr.fields, &item_decoder_assigner_ast(&1, ctx))
quote do
with do
<<unquote_splicing(field_decoders), _::unquote(descr.pad), unquote(@data_var)::binary>> = unquote(@data_var)
ret = %{unquote_splicing(field_assigners)}
{ret, unquote(@data_var)}
end
end
end
def item_decoder_ast(%{signed: true} = item, _ctx) do
var = Macro.var(item.name, __MODULE__)
quote do: unquote(var)::signed-integer-unquote(item.size)
end
def item_decoder_ast(%{signed: false} = item, _ctx) do
var = Macro.var(item.name, __MODULE__)
quote do: unquote(var)::unsigned-integer-unquote(item.size)
end
def item_decoder_assigner_ast(item, _ctx) do
var = Macro.var(item.name, __MODULE__)
{item.name, var}
end

# Encoder AST pass

def encoder_ast(descr, ctx) do
field_encoders = Enum.map(descr.fields, &item_encoder_ast(&1, ctx))
quote do
<<unquote_splicing(field_encoders), 0::unquote(descr.pad)>>
end
end
def item_encoder_ast(%{signed: true} = item, _ctx) do
quote do: unquote(@input_var)[unquote(item.name)]::signed-integer-unquote(item.size)
end
def item_encoder_ast(%{signed: false} = item, _ctx) do
quote do: unquote(@input_var)[unquote(item.name)]::unsigned-integer-unquote(item.size)
end

end
16 changes: 13 additions & 3 deletions lib/type/bool.ex
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ defmodule ProtoDef.Type.Bool do
%__MODULE__{}
end

def structure(_descr, _ctx), do: :bool
def structure(_descr, _ctx), do: __MODULE__

def assign_vars(descr, num, ctx) do
{ident, num} = ProtoDef.Compiler.AssignIdents.make_ident(num, ctx)
Expand All @@ -18,8 +18,18 @@ defmodule ProtoDef.Type.Bool do
def decoder_ast(descr, ctx) do
quote do
with do
<<val::unsigned-integer-1*8, data::binary>> = data
{val == 1, data}
<<val::unsigned-integer-1*8, unquote(@data_var)::binary>> = unquote(@data_var)
{val == 1, unquote(@data_var)}
end
end
end

def encoder_ast(descr, ctx) do
quote do
if unquote(@input_var) do
<<1::1*8>>
else
<<0::1*8>>
end
end
end
Expand Down
25 changes: 19 additions & 6 deletions lib/type/buffer.ex
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule ProtoDef.Type.Buffer do
alias ProtoDef.Compiler.Preprocess
use ProtoDef.Type

@behaviour ProtoDef.Type
alias ProtoDef.Compiler.Preprocess

defstruct count: nil, ident: nil, count_ident: nil

Expand All @@ -11,7 +11,7 @@ defmodule ProtoDef.Type.Buffer do
}
end

def structure(_descr, _ctx), do: :buffer
def structure(_descr, _ctx), do: __MODULE__

def assign_vars(descr, num, ctx) do
{count, count_ident, num} = ProtoDef.Compiler.AssignIdents.assign_count(descr.count, num, ctx)
Expand All @@ -35,10 +35,23 @@ defmodule ProtoDef.Type.Buffer do
count_ast = ProtoDef.Compiler.Count.decoder_ast(descr.count, ctx)
quote do
with do
{count, data} = unquote(count_ast)
<<val::binary-size(count), data::binary>> = data
{val, data}
{count, unquote(@data_var)} = unquote(count_ast)
<<val::binary-size(count), unquote(@data_var)::binary>> = unquote(@data_var)
{val, unquote(@data_var)}
end
end
end

def encoder_ast(descr, ctx) do
count_var = Macro.var(:count, ProtoDef.Type.Buffer)
count_encoder = ProtoDef.Compiler.Count.encoder_ast(descr.count, count_var, ctx)
quote do
with do
unquote(count_var) = IO.iodata_length(unquote(@input_var))
count_head = unquote(count_encoder)
[count_head, unquote(@input_var)]
end
end
end

end
2 changes: 1 addition & 1 deletion lib/type/container.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ defmodule ProtoDef.Type.Container do

%{
anon: anon,
name: (if name, do: String.to_atom(name)),
name: (if name, do: ctx.parse_name.(name)),
type: Preprocess.process_type(type, ctx),
ident: nil,
}
Expand Down
33 changes: 31 additions & 2 deletions lib/type/cstring.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,38 @@
defmodule ProtoDef.Type.CString do
@behaviour ProtoDef.Type
use ProtoDef.Type

defstruct []
defstruct ident: nil

def preprocess(nil, _ctx) do
%__MODULE__{}
end

def structure(_, _), do: __MODULE__

def decoder_ast(_descr, _ctx) do
quote do
with do
{:ok, {str, rest}} = ProtoDef.Type.CString.decode_string(unquote(@data_var))
{str, rest}
end
end
end

def encoder_ast(_descr, _ctx) do
quote do
with do
# This makes sure there are no null bytes in the string
:error = ProtoDef.Type.CString.decode_string(unquote(@input_var))
[unquote(@input_var), 0]
end
end
end

def decode_string(bin, num \\ 0) do
case bin do
<<str::binary-size(num), 0, rest::binary>> -> {:ok, {str, rest}}
_ when byte_size(bin) > num -> decode_string(bin, num+1)
_ -> :error
end
end
end
22 changes: 18 additions & 4 deletions lib/type/option.ex
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
defmodule ProtoDef.Type.Option do
use ProtoDef.Type

alias ProtoDef.Compiler.Preprocess
alias ProtoDef.Compiler.Structure

@behaviour ProtoDef.Type

defstruct type: nil, ident: nil

def preprocess(args, ctx) do
Expand Down Expand Up @@ -33,14 +33,28 @@ defmodule ProtoDef.Type.Option do
item_ast = ProtoDef.Compiler.GenAst.decoder(descr.type, ctx)
quote do
with do
<<has_field::unsigned-integer-1*8, data::binary>> = data
<<has_field::unsigned-integer-1*8, unquote(@data_var)::binary>> = unquote(@data_var)
if has_field == 1 do
unquote(item_ast)
else
{nil, data}
{nil, unquote(@data_var)}
end
end
end
end

def encoder_ast(descr, ctx) do
item_ast = ProtoDef.Compiler.GenAst.encoder(descr.type, ctx)
quote do
with do
has_field = if unquote(@input_var), do: 1, else: 0
if has_field == 1 do
[<<has_field::1*8>>, unquote(item_ast)]
else
<<has_field::1*8>>
end
end
end
end

end
Loading

0 comments on commit a780858

Please sign in to comment.