Packages

C compiler built with Elixir. This library will only allow you to compile basic C programs.

Current section

Files

Jump to
exc lib modules parser.ex
Raw

lib/modules/parser.ex

defmodule Parser do
@moduledoc """
Parses the Output Token List (OTL) from `Lexer.tokenize/1` into an Output
Abstract Syntax Tree (OAST).
"""
@doc """
Returns a tuple containing a status token, the Output Abstract Syntax Tree (OAST),
a token list and a possible error cause.
## Specs
```otl``` is the Output Token List which is a list of the tokens found in the
source code string. This list is generated by the `Lexer` in `Lexer.tokenize/1`.
```gast``` is a General Abstract Syntax Tree which is list of `Structs.Node`
that contains the source language grammar production rules.
Returns the following elements:
+ ```status_atom``` : indicates if the parsing could be completed successfully.
If not, then the following error tokens are returned:
+ ```:token_missing_error``` : thrown when the source code is missing
something or the order of the tokens is not correct. For example:
```
int main() {
2;
}
```
where the token ```return```is missing.
+ ```:token_not_absorbed``` : thrown when the structure is correct but it
has an extra token. For example:
```
int main() {
return 2;
} main
```
where the token ```main```is unnecessary.
+ ```output_ast``` : abstract syntax tree representing the source code.
+ ```token_list``` : token list found in the source code.
+ ```error_cause``` : description of the error cause.
"""
def parse(otl, gast) do
ps_m = generate_possible_structure_map(gast)
root_AST = generate_root_ast()
{result_token, oast, tl, error_cause} = my_structure_matches(root_AST, otl, ps_m)
if result_token === :ok and tl === [] do
{:ok,oast,tl,error_cause}
else
if result_token === :error do
{:token_missing_error,oast,tl,error_cause}
else
{:token_not_absorbed_error,oast,tl,error_cause}
end
end
end
defp my_structure_matches(cs,tl,ps_m) do
{result_token_1, tl_1, absorbed_token} = my_token_matches(cs,tl)
if result_token_1 === :ok do
{result_token_2, tl_2, children_list,error_cause} =
my_children_match(cs,tl_1,ps_m)
if result_token_2 === :ok do
d_cs = specifize_structure(cs,absorbed_token,children_list)
{:ok,d_cs,tl_2,nil}
else
#there was no if
if error_cause === nil do
{:error,cs,tl,cs}
else
{:error,cs,tl,error_cause}
end
#{:error,cs,tl,incoming_error} #:structure-does-not-match-expectation
end
else
{:error,cs,tl,nil} #:Could not absorb token of structure
end
end
defp my_children_match(cs,tl,ps_m) do
children_list = cs.children
next_child_match(children_list,tl,[],ps_m)
end
defp next_child_match([],tl,previous_children,_ps_m) do
{:ok, tl, previous_children, nil}
end
defp next_child_match(cl,tl,previous_children,ps_m) do
# IO.inspect(cl, label: "The list is: ") # Commented line to avoid printing
[child | cl_1] = cl
possible_structures = ps_m[child["class"]]
{result_token, matched_structure, tl_1, incoming_error} =
check_ps(possible_structures,tl,nil,ps_m)
if result_token === :ok do
next_child_match(cl_1,tl_1,previous_children++[matched_structure],ps_m)
else
{:error,tl,previous_children++[child],incoming_error}
#if incoming_error === nil do
# {:error,tl,previous_children++[child],child}
#else
# {:error,tl,previous_children++[child],incoming_error}
#end
end
end
defp check_ps([],tl,error_cause,_ps_m) do
{:error,nil,tl,error_cause}
end
defp check_ps(ps,tl,_error_cause,ps_m) do
[candidate_structure | ps_1] = ps
{result_token,current_candidate,tl_1,error_cause_1} =
my_structure_matches(candidate_structure,tl,ps_m)
if result_token === :ok do
{:ok,current_candidate,tl_1,nil}
else
check_ps(ps_1,tl,error_cause_1,ps_m)
end
end
defp my_token_matches(cs,[]) do
if cs.token === nil do
{:ok,[],nil}
else
{:error,[],nil}
end
end
defp my_token_matches(cs,tl) do
[absorbed_token | tl_1] = tl
if cs.token === nil do
{:ok,tl,nil}
else
if cs.token === absorbed_token.tag do
{:ok,tl_1,absorbed_token}
else
{:error,tl,absorbed_token}
end
end
end
defp specifize_structure(cs,absorbed_token,children_list) do
%Structs.Node{
class: cs.class,
token: absorbed_token,
tag: cs.tag,
asm: cs.asm,
children: children_list
}
end
defp generate_possible_structure_map(gast) do
keys = (Enum.map(gast, fn x -> [x.class] end) |> List.flatten |> Enum.uniq)
Enum.map(
keys, fn k -> {
k,
Enum.filter(
gast,
fn y -> Enum.member?(
List.flatten([y.class]),
String.replace(k,"*",""))
end)
} end
)
|> Enum.map(
fn {k,value_list} ->
if String.contains?(k, "*"),
do: {k,value_list ++ [generate_null_ast(k)]},
else: {k,value_list}
end
)
|> Map.new
end
defp generate_root_ast() do
%Structs.Node{
class: :root,
token: nil,
tag: :root,
children: [%{"class"=>"program-root", "tag"=>"program-root"}],
asm: "movq %:0, %rax"
}
end
defp generate_null_ast(optional_class) do
%Structs.Node{
class: String.replace(optional_class,"*",""),
token: nil,
tag: "no_match_for_optional_substructure",
children: [],
asm: ""
}
end
end