-module(gecko). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/gecko.gleam"). -export([gen_naked/2, gen_rule/2, next_opt/2, next/2]). -export_type([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 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.gleam", 52). ?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, _, _) -> S_len = string:length(S), Input_len = string:length(Input), case (S_len =< Input_len) andalso gleam_stdlib:string_starts_with( Input, S ) of true -> {some, {gleam@string:drop_start(Input, S_len), {token, naked, Constructor, S}}}; false -> none end end. -file("src/gecko.gleam", 86). ?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.gleam", 156). -spec try_toks( list(fun((binary(), binary(), fun((binary()) -> DRU)) -> gleam@option:option({binary(), token(DRU)}))), binary(), DRU ) -> gleam@option:option({binary(), DRU}). try_toks(Toks, Source, Eof) -> case Toks of [] -> none; [F | Rest] -> case F(Source, <<""/utf8>>, fun(_) -> Eof end) of {some, {Remaining, {token, _, Constructor, Word}}} -> Token = Constructor(Word), {some, {Remaining, Token}}; none -> try_toks(Rest, Source, Eof) end end. -file("src/gecko.gleam", 136). ?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" ). -spec next_opt(lexer(DRP), binary()) -> gleam@option:option({binary(), DRP}). next_opt(Lexer, Source) -> {lexer, Toks, Eof} = Lexer, Trimmed = gleam@string:trim_start(Source), case gleam@string:is_empty(Trimmed) of true -> {some, {Trimmed, Eof}}; false -> try_toks(Toks, Trimmed, Eof) end. -file("src/gecko.gleam", 149). ?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" ). -spec next(lexer(DRS), binary()) -> {binary(), DRS}. next(Lexer, Source) -> case next_opt(Lexer, Source) of {some, {Source@1, Tk}} -> {Source@1, Tk}; none -> {<<""/utf8>>, erlang:element(3, Lexer)} end.