Current section
Files
Jump to
Current section
Files
src/gecko@lexer.erl
-module(gecko@lexer).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/gecko/lexer.gleam").
-export([gen_naked/2, gen_rule/2, advance_loc/2, next_opt/3, next/3]).
-export_type([loc/0, naked/0, token/1, lexer/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type loc() :: {loc, binary(), integer(), integer()}.
-type naked() :: {rule, binary()} | naked.
-type token(DRG) :: {token, naked(), fun((binary()) -> DRG), binary()}.
-type lexer(DRH) :: {lexer,
list(fun((binary(), binary(), fun((binary()) -> DRH)) -> gleam@option:option({binary(),
token(DRH)}))),
DRH}.
-file("src/gecko/lexer.gleam", 57).
?DOC(
" Creates a token function that matches a literal string.\n"
" \n"
" ## Example\n"
" \n"
" ```gleam\n"
" let lparen = gen_naked(\"(\", fn(_) { OParen })\n"
" ```\n"
" \n"
" ## Parameters\n"
" \n"
" - `s`: The literal string to match\n"
" - `constructor`: Function that takes matched text and returns a token\n"
" \n"
" ## Returns\n"
" \n"
" A `TokenFn` that matches the literal string at the start of input.\n"
).
-spec gen_naked(binary(), fun((binary()) -> DRL)) -> fun((binary(), binary(), fun((binary()) -> DRL)) -> gleam@option:option({binary(),
token(DRL)})).
gen_naked(S, Constructor) ->
fun(Input, _, _) -> case gleam_stdlib:string_starts_with(Input, S) of
true ->
Remaining = gleam@string:drop_start(Input, string:length(S)),
{some, {Remaining, {token, naked, Constructor, S}}};
false ->
none
end end.
-file("src/gecko/lexer.gleam", 91).
?DOC(
" Creates a token function that matches a regex pattern.\n"
" \n"
" Uses the `gleam_regexp` library for regex matching.\n"
" Supports standard regex syntax as provided by the library.\n"
" \n"
" ## Example\n"
" \n"
" ```gleam\n"
" let identifier = gen_rule(\"[a-zA-Z][a-zA-Z0-9_]*\", Ident)\n"
" let number = gen_rule(\"[1-9][0-9]*\", fn(s) { Number(parse_int(s)) })\n"
" ```\n"
" \n"
" ## Parameters\n"
" \n"
" - `regex`: A regex pattern string\n"
" - `constructor`: Function that takes matched text and returns a token\n"
" \n"
" ## Returns\n"
" \n"
" A `TokenFn` that matches the regex pattern at the start of input.\n"
).
-spec gen_rule(binary(), fun((binary()) -> DRN)) -> fun((binary(), binary(), fun((binary()) -> DRN)) -> gleam@option:option({binary(),
token(DRN)})).
gen_rule(Regex, Constructor) ->
fun(Input, _, _) ->
case gleam@regexp:compile(Regex, {options, false, false}) of
{ok, Compiled} ->
case gleam@regexp:scan(Compiled, Input) of
[Match | _] ->
case gleam_stdlib:string_starts_with(
Input,
erlang:element(2, Match)
) of
true ->
Matched_text = erlang:element(2, Match),
Remaining = gleam@string:drop_start(
Input,
string:length(Matched_text)
),
{some,
{Remaining,
{token,
{rule, Regex},
Constructor,
Matched_text}}};
false ->
none
end;
_ ->
none
end;
{error, _} ->
none
end
end.
-file("src/gecko/lexer.gleam", 198).
?DOC(" Advance a Loc by a string, updating row/col for newlines.\n").
-spec advance_loc(loc(), binary()) -> loc().
advance_loc(Loc, Text) ->
{loc, File, Row, Col} = Loc,
Lines = gleam@string:split(Text, <<"\n"/utf8>>),
N = erlang:length(Lines),
case N of
0 ->
Loc;
1 ->
First = case gleam@list:first(Lines) of
{ok, S} ->
S;
{error, _} ->
<<""/utf8>>
end,
{loc, File, Row, Col + string:length(First)};
N@1 when N@1 >= 2 ->
Last = case gleam@list:last(Lines) of
{ok, S@1} ->
S@1;
{error, _} ->
<<""/utf8>>
end,
{loc, File, (Row + N@1) - 1, string:length(Last) + 1};
_ ->
Loc
end.
-file("src/gecko/lexer.gleam", 176).
-spec try_toks(
list(fun((binary(), binary(), fun((binary()) -> DRU)) -> gleam@option:option({binary(),
token(DRU)}))),
binary(),
loc(),
DRU
) -> gleam@option:option({binary(), loc(), DRU}).
try_toks(Toks, Source, Loc, Eof) ->
case Toks of
[] ->
none;
[F | Rest] ->
case F(Source, <<""/utf8>>, fun(_) -> Eof end) of
{some, {Remaining, {token, _, Constructor, Word}}} ->
Token = Constructor(Word),
New_loc = advance_loc(Loc, Word),
{some, {Remaining, New_loc, Token}};
none ->
try_toks(Rest, Source, Loc, Eof)
end
end.
-file("src/gecko/lexer.gleam", 145).
?DOC(
" Attempt to read the next token from the start of `source`.\n"
" - If `source` is empty, returns `Some(#(source, eof))`.\n"
" - Otherwise, tries each token function in order and returns the first match.\n"
" - If no token matches the current input, returns `None`.\n"
" Attempt to read the next token from the start of `source`, tracking position.\n"
" - If `source` is empty, returns `Some(#(source, loc, eof))`.\n"
" - Otherwise, tries each token function in order and returns the first match.\n"
" - If no token matches the current input, returns `None`.\n"
).
-spec next_opt(lexer(DRP), binary(), loc()) -> gleam@option:option({binary(),
loc(),
DRP}).
next_opt(Lexer, Source, Loc) ->
{lexer, Toks, Eof} = Lexer,
Trimmed = gleam@string:trim_start(Source),
Trimmed_len = string:length(Source) - string:length(Trimmed),
Skipped = gleam@string:slice(Source, 0, Trimmed_len),
New_loc = advance_loc(Loc, Skipped),
case gleam@string:is_empty(Trimmed) of
true ->
{some, {Trimmed, New_loc, Eof}};
false ->
try_toks(Toks, Trimmed, New_loc, Eof)
end.
-file("src/gecko/lexer.gleam", 169).
?DOC(
" Attempt to read the next token from the start of `source`.\n"
" - If `source` is empty, returns `#(\"\", eof)`.\n"
" - Otherwise, tries each token function in order and returns the first match.\n"
" - If no token matches the caurrent input, returns `#(\"\", eof)`.\n"
" Attempt to read the next token from the start of `source`, tracking position.\n"
" - If `source` is empty, returns `#(\"\", loc, eof)`.\n"
" - Otherwise, tries each token function in order and returns the first match.\n"
" - If no token matches the current input, returns `#(\"\", loc, eof)`.\n"
).
-spec next(lexer(DRS), binary(), loc()) -> {binary(), loc(), DRS}.
next(Lexer, Source, Loc) ->
case next_opt(Lexer, Source, Loc) of
{some, {Source@1, Loc@1, Tk}} ->
{Source@1, Loc@1, Tk};
none ->
{<<""/utf8>>, Loc, erlang:element(3, Lexer)}
end.