aboutsummaryrefslogblamecommitdiffstats
path: root/lib/diameter/src/compiler/diameter_spec_scan.erl
blob: bc0448882ab46ea3e659446336c9207e620e121f (plain) (tree)




























































































































































                                                                         
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

-module(diameter_spec_scan).

%%
%% Functions used by the spec file parser in diameter_spec_util.
%%

-export([split/1,
         split/2,
         parse/1]).

%%% -----------------------------------------------------------
%%% # parse/1
%%%
%%% Output: list of Token
%%%
%%%         Token = '{' | '}' | '<' | '>' | '[' | ']'
%%%               | '*' | '::=' | ':' | ',' | '-'
%%%               | {name, string()}
%%%               | {tag, atom()}
%%%               | {number, integer() >= 0}
%%%
%%% Tokenize a string. Fails if the string does not parse.
%%% -----------------------------------------------------------

parse(S) ->
    parse(S, []).

%% parse/2

parse(S, Acc) ->
    acc(split(S), Acc).

acc({T, Rest}, Acc) ->
    parse(Rest, [T | Acc]);
acc("", Acc) ->
    lists:reverse(Acc).

%%% -----------------------------------------------------------
%%% # split/2
%%%
%%% Output: {list() of Token, Rest}
%%%
%%% Extract a specified number of tokens from a string. Returns a list
%%% of length less than the specified number if there are less than
%%% this number of tokens to be parsed.
%%% -----------------------------------------------------------

split(Str, N)
  when N >= 0 ->
    split(N, Str, []).

split(0, Str, Acc) ->
    {lists:reverse(Acc), Str};

split(N, Str, Acc) ->
    case split(Str) of
        {T, Rest} ->
            split(N-1, Rest, [T|Acc]);
        "" = Rest ->
            {lists:reverse(Acc), Rest}
    end.

%%% -----------------------------------------------------------
%%% # split/1
%%%
%%% Output: {Token, Rest} | ""
%%%
%%% Extract the next token from a string.
%%% -----------------------------------------------------------

split("" = Rest) ->
    Rest;

split("::=" ++ T) ->
    {'::=', T};

split([H|T])
  when H == ${; H == $};
       H == $<; H == $>;
       H == $[; H == $];
       H == $*; H == $:; H == $,; H == $- ->
    {list_to_atom([H]), T};

split([H|T]) when $A =< H, H =< $Z;
                  $0 =< H, H =< $9 ->
    {P, Rest} = splitwith(fun is_name_ch/1, [H], T),
    Tok = try
              {number, read_int(P)}
          catch
              error:_ ->
                  {name, P}
          end,
    {Tok, Rest};

split([H|T]) when $a =< H, H =< $z ->
    {P, Rest} = splitwith(fun is_name_ch/1, [H], T),
    {{tag, list_to_atom(P)}, Rest};

split([H|T]) when H == $\t;
                  H == $\s;
                  H == $\n ->
    split(T).

%% read_int/1

read_int([$0,X|S])
  when X == $X;
       X == $x ->
    {ok, [N], []} = io_lib:fread("~16u", S),
    N;

read_int(S) ->
    list_to_integer(S).

%% splitwith/3

splitwith(Fun, Acc, S) ->
    split([] /= S andalso Fun(hd(S)), Fun, Acc, S).

split(true, Fun, Acc, [H|T]) ->
    splitwith(Fun, [H|Acc], T);
split(false, _, Acc, S) ->
    {lists:reverse(Acc), S}.

is_name_ch(C) ->
    is_alphanum(C) orelse C == $- orelse C == $_.

is_alphanum(C) ->
    is_lower(C) orelse is_upper(C) orelse is_digit(C).

is_lower(C) ->
    $a =< C andalso C =< $z.

is_upper(C) ->
    $A =< C andalso C =< $Z.

is_digit(C) ->
    $0 =< C andalso C =< $9.