Skip to content
This repository has been archived by the owner on Jun 6, 2021. It is now read-only.

Add attribute parsing #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 112 additions & 2 deletions lib/compiler/syntax/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ defmodule Flect.Compiler.Syntax.Parser do

parse_decls(state, [{:declaration, decl} | decls])
{v, token, state} when v in [:pub, :priv] ->
{decl, state} = case expect_token(state, [:fn, :struct, :union, :enum, :type, :trait,
:impl, :glob, :tls, :macro], "declaration") do
{decl, state} = case expect_token(state, [:fn, :struct, :union, :enum, :trait,
:impl, :glob, :tls, :macro, :at], "declaration") do
{:fn, _, _} -> parse_fn_decl(state, token)
{:struct, _, _} -> parse_struct_decl(state, token)
{:union, _, _} -> parse_union_decl(state, token)
Expand All @@ -136,6 +136,7 @@ defmodule Flect.Compiler.Syntax.Parser do
{:glob, _, _} -> parse_glob_decl(state, token)
{:tls, _, _} -> parse_tls_decl(state, token)
{:macro, _, _} -> parse_macro_decl(state, token)
{:at, _, _} -> parse_attribute(state, token)
end

parse_decls(state, [{:declaration, decl} | decls])
Expand Down Expand Up @@ -1834,6 +1835,115 @@ defmodule Flect.Compiler.Syntax.Parser do
{new_node(:identifier_expr, name.location(), [], [{:name, name} | ty_args]), state}
end

@spec parse_attribute(state(), token()) :: return_n()
defp parse_attribute(state, visibility) do
{_, tok_at, state} = expect_token(state, :at, "at")
{_, tok_open, state} = expect_token(state, :bracket_open, "opening bracket")
{name, state} = parse_attribute_name(state)

{attr_args, state} = case next_token(state) do
{:paren_open, _, _} ->
{attr_args, state} = parse_attribute_arguments(state)
{[arguments: attr_args], state}
_ -> {[], state}
end
{_, tok_close, state} = expect_token(state, :bracket_close, "closing bracket")

{new_node(:attribute, name.location(), [at_symbol: tok_at] ++ [opening_bracket: tok_open] ++
[closing_bracket: tok_close], [name: name] ++ attr_args), state}
end

@spec parse_attribute_name(state()) :: return_n()
defp parse_attribute_name(state) do
case next_token(state) do
{v, tok, state} when v == :identifier ->
{new_node(:attribute_name, tok.location(), [name: tok]), state}

{_, tok, state} ->
if Flect.Compiler.Syntax.Lexer.keyword?(atom_to_binary(tok.type())) do
{new_node(:attribute_name, tok.location(), [name: tok]), state}

else
raise_error(tok.location(), "Expected keyword or identifier")
end
end
end

@spec parse_attribute_arguments(state()) :: return_n()
defp parse_attribute_arguments(state) do
{_, tok_open, state} = expect_token(state, :paren_open, "opening parenthesis")
{attrs, toks, state} = parse_attribute_argument_list(state, [])
{_, tok_close, state} = expect_token(state, :paren_close, "closing parenthesis")

attrs = Enum.reverse(attrs)
attrs = lc attr inlist attrs, do: {:attribute_argument, attr}
toks = lc tok inlist toks, do: {:comma, tok}

{new_node(:attribute_arguments, tok_open.location(),
[opening_parenthesis: tok_open] ++ toks ++ [closing_parenthesis: tok_close], attrs), state}
end

@spec parse_attribute_argument_list(state(), [ast_node()], [token()]) :: return_mt()
defp parse_attribute_argument_list(state, attrs, tokens // []) do
{attr, state} = parse_attribute_argument(state)

case next_token(state) do
{:comma, tok, state} -> parse_attribute_argument_list(state, [attr | attrs], [tok | tokens])
_ -> {[attr | attrs], tokens, state}
end
end

@spec parse_attribute_argument(state()) :: return_n()
defp parse_attribute_argument(state) do
{name, state} = parse_attribute_name(state)

{attr, state} = case next_token(state) do
{:assign, _, _} ->
{attr_val, state} = parse_attribute_value(state)
{[attribute_value: attr_val], state}
_ ->
{[], state}
end

{new_node(:attribute_argument, name.location(), [], [name: name] ++ attr), state}
end

@spec parse_attribute_value(state()) :: return_n()
defp parse_attribute_value(state) do
{_, tok_assign, state} = expect_token(state, :assign, "assign")

{attr_val, state} = case next_token(state) do
{v, token, state} when v == :paren_open ->
{attr_args, state} = parse_attribute_arguments(state)
{[attribute_argument: attr_args], state}
_ ->
{attr_lit, state} = parse_attribute_literal(state)
{[attribute_argument: attr_lit], state}
end

{new_node(:attribute_value, tok_assign.location(), [assign: tok_assign], attr_val), state}
end

@spec parse_attribute_literal(state()) :: return_n()
defp parse_attribute_literal(state) do
case next_token(state) do
{v, tok, state} when v in [:u8, :u16, :u32, :u64, :i8, :i16, :i32, :i64] ->
{new_node(:attribute_typed_int_literal, tok.location(), [name: tok]), state}

{w, tok, state} when w in [:f32, :f64] ->
{new_node(:attribute_typed_float_literal, tok.location(), [name: tok]), state}

{x, tok, state} when x == :character ->
{new_node(:attribute_char_literal, tok.location(), [name: tok]), state}

{y, tok, state} when y == :string ->
{new_node(:attribute_string_literal, tok.location(), [name: tok]), state}

{_, tok, state} ->
raise_error(tok.location(), "Expected typed int literal, typed float literal, character or string")
end
end

@spec next_token(state(), boolean()) :: {atom(), token(), state()} | :eof
defp next_token({tokens, loc}, eof // false) do
case tokens do
Expand Down
3 changes: 3 additions & 0 deletions tests/parse-pass/attributes.fl
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod Bar {
pub @ [ enum (label = 'f', type = 2:u16) ]
}
42 changes: 42 additions & 0 deletions tests/parse-pass/attributes.fl.0.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module_declaration (1,5) [ "pub" (1,1), "mod" (1,5), "{" (1,13), "}" (3,1) ]
{
qualified_name (1,9) [ ]
{
simple_name (1,9) [ "Bar" (1,9) ]
{
}
}
attribute (2,13) [ "@" (2,9), "[" (2,11), "]" (2,46) ]
{
attribute_name (2,13) [ "enum" (2,13) ]
{
}
attribute_arguments (2,18) [ "(" (2,18), "," (2,30), ")" (2,44) ]
{
attribute_argument (2,19) [ ]
{
attribute_name (2,19) [ "label" (2,19) ]
{
}
attribute_value (2,25) [ ]
{
attribute_char_literal (2,27) [ "'f'" (2,27) ]
{
}
}
}
attribute_argument (2,32) [ ]
{
attribute_name (2,32) [ "type" (2,32) ]
{
}
attribute_value (2,37) [ ]
{
attribute_typed_int_literal (2,39) [ "2" (2,39) ]
{
}
}
}
}
}
}