Current section
Files
Jump to
Current section
Files
src/sql_lex.xrl
%% -----------------------------------------------------------------------------
%%
%% sql_lex.xrl: SQL - lexer definition.
%%
%% Copyright (c) 2012-20 Konnexions GmbH. All Rights Reserved.
%%
%% -----------------------------------------------------------------------------
%% -*- erlang -*-
Definitions.
W = \s\r\n
A = [A-Za-z0-9,_{W}]
OS = (.*|[{W}]*)
Rules.
% erlang funcs
(fun[{W}]*\({A}*\){OS}*\->{OS}*end\.) : match_fun(TokenLine, TokenChars).
(fun[{W}]+['A-Za-z0-9_]+:['A-Za-z0-9_]+\/[0-9]+\.) : {token, {'STRING', TokenLine, TokenChars}}.
% database link
(\"@[A-Za-z0-9_\$#\.@]+\") : {token, {'DBLINK', TokenLine, TokenChars}}.
% strings
(\'([^\']*(\'\')*)*\') : {token, {'STRING', TokenLine, TokenChars}}.
(\"((\$|[^\"]*)*(\"\")*)*\") : {token, {'NAME', TokenLine, TokenChars}}.
% hint
((\/\*)[^\*\/]*(\*\/)) : {token, {'HINT', TokenLine, TokenChars}}.
% punctuation
(!=|\^=|<>|<|>|<=|>=) : {token, {'COMPARISON', TokenLine, list_to_atom(TokenChars)}}.
([=\|\-\+\*\/\(\)\,\.\;]|(\|\|)|(:=)|(=>)|(div)) : {token, {list_to_atom(TokenChars), TokenLine}}.
% JSON
(\|[:{\[#]([^\|]*)+\|) : parse_json(TokenLine, TokenChars).
% names
[A-Za-z][A-Za-z0-9_\$@~]* : match_any(TokenChars, TokenLen, TokenLine, ?TOKENPATTERNS).
% parameters
(\:[A-Za-z0-9_\.][A-Za-z0-9_\.]*) : {token, {'PARAMETER', TokenLine, TokenChars}}.
% numbers
([0-9]+) : {token, {'INTNUM', TokenLine, TokenChars}}.
((([\.][0-9]+)|([0-9]+[\.]?[0-9]*))([eE][+-]?[0-9]+)?[fFdD]?)
: {token, {'APPROXNUM', TokenLine, TokenChars}}.
% skips
([\s\t\r\n]+) : skip_token. %% white space
% comments
((\-\-).*[\n]) : skip_token.
%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Erlang code.
%% -----------------------------------------------------------------------------
%%
%% sqllexer.erl: SQL - lexer.
%%
%% Copyright (c) 2012-20 Konnexions GmbH. All Rights Reserved.
%%
%% -----------------------------------------------------------------------------
-export([reserved_keywords/0]).
-define(NODEBUG, true).
-include("sql_lex.hrl").
reserved_keywords() -> [T || {_, T} <- ?KEYWORDS].
match_any(TokenChars, TokenLen, _TokenLine, []) ->
{token, {'NAME', TokenLen, TokenChars}};
match_any(TokenChars, TokenLen, TokenLine, [{P, T} | TPs]) ->
case re:run(TokenChars, P, [{capture, first, list}]) of
{match, [_]} ->
if (T =:= 'FUNS') orelse
(T =:= 'UFUN') ->
{token, {T, TokenLine, list_to_atom(TokenChars)}};
true -> {token, {T, TokenLine}}
end;
nomatch -> match_any(TokenChars, TokenLen, TokenLine, TPs)
end.
match_fun(TokenLine, TokenChars) ->
{match, [MatchedFunStr]} = re:run(TokenChars, "^fun.*end\\.", [ungreedy, dotall, {capture, all, list}]),
{token, {'STRING', TokenLine, MatchedFunStr}, string:sub_string(TokenChars, length(MatchedFunStr) + 1)}.
parse_json(TokenLine, TokenCharsIn) ->
TokenChars = string:substr(TokenCharsIn, 2, length(TokenCharsIn) - 2),
?D("TokenChars=~p", [TokenChars]),
parse_json(TokenLine, TokenChars, "", "", 0).
parse_json(TokenLine, [], Json, PushBack, 0) ->
{token, {'JSON', TokenLine, lists:reverse(Json)}, PushBack};
parse_json(_TokenLine, [], Json, _PushBack, Open) ->
{error, lists:flatten(
io_lib:format("malformed JSON path '~s',"
" ~p bracket(s) not closed"
, [lists:reverse(Json), Open]))};
parse_json(TokenLine, [T | TokenChars], Json, PushBack, Open)
when T =:= $]; T =:= $}; T =:= $) ->
?D("T=~p TChrs=~p Json=~p PB=~p Open=~p", [[T], TokenChars, Json, PushBack, Open]),
if Open > 0 ->
parse_json(TokenLine, TokenChars, [T | Json], PushBack, Open - 1);
true ->
parse_json(TokenLine, [], Json, [T | TokenChars], Open)
end;
parse_json(TokenLine, [T | TokenChars], Json, PushBack, Open)
when T =:= $[; T =:= ${; T =:= $( ->
?D("T=~p TChrs=~p Json=~p PB=~p Open=~p", [[T], TokenChars, Json, PushBack, Open]),
parse_json(TokenLine, TokenChars, [T | Json], PushBack, Open + 1);
parse_json(TokenLine, [T | TokenChars], Json, PushBack, Open)
when Open > 0 ->
?D("T=~p TChrs=~p Json=~p PB=~p Open=~p", [[T], TokenChars, Json, PushBack, Open]),
parse_json(TokenLine, TokenChars, [T | Json], PushBack, Open);
parse_json(TokenLine, [T | TokenChars], Json, PushBack, Open)
when (Open =:= 0) andalso (
(T =:= $:) orelse (T =:= $#)
orelse (T =:= $_) orelse (T =:= $.)
orelse ((T >= $A) andalso (T =< $Z))
orelse ((T >= $a) andalso (T =< $z))
orelse ((T >= $0) andalso (T =< $9))
) ->
?D("T=~p TChrs=~p Json=~p PB=~p Open=~p", [[T], TokenChars, Json, PushBack, Open]),
parse_json(TokenLine, TokenChars, [T | Json], PushBack, Open);
parse_json(TokenLine, [T | TokenChars], Json, PushBack, Open)
when (Open =:= 0) ->
?D("T=~p TChrs=~p Json=~p PB=~p Open=~p", [[T], TokenChars, Json, PushBack, Open]),
{NewTokenChars, NewJson, NewPushBack} =
case T of
T when [T] =:= " ";T =:= $\n;T =:= $\t;T =:= $\r -> % white space characters
case re:run(TokenChars, "^([[:space:]]*)(.*)", [{capture, [1, 2], list}, dotall]) of
{match, [WhiteSpace, [F | _] = Rest]} when F =:= $[;F =:= ${;F =:= $( ->
{Rest, lists:reverse(WhiteSpace) ++ [T | Json], PushBack};
_ ->
{[], Json, [T | TokenChars]}
end;
_ -> {[], Json, [T | TokenChars]}
end,
?D("NewTokenChars=~p NewJson=~p NewPushBack=~p", [NewTokenChars, NewJson, NewPushBack]),
parse_json(TokenLine, NewTokenChars, NewJson, NewPushBack, Open).