<commit_before>-module(ephp).
-compile([warnings_as_errors]).

-export([
    context_new/0,
    register_var/3,
    register_fun/4,
    register_module/2,
    eval/2,

    main/1   %% for escriptize
]).

-include("ephp.hrl").

-spec context_new() -> {ok, context()}.

context_new() ->
    Modules = ?MODULES,
    case ephp_context:start_link() of
        {ok, Ctx} -> 
            [ register_module(Ctx, Module) || Module <- Modules ],
            {ok, Ctx};
        Error ->
            Error
    end.

-type values() :: integer() | binary() | float().

-spec register_var(Ctx :: context(), Var :: binary(), Value :: values()) ->
    ok | {error, reason()}.

register_var(Ctx, _Var, _Value) when not is_pid(Ctx) ->
    {error, enoctx};

register_var(Ctx, Var, Value) when 
        is_integer(Value) orelse 
        is_float(Value) orelse
        is_binary(Value) orelse
        ?IS_DICT(Value) ->
    ephp_context:set(Ctx, #variable{name=Var}, Value);

register_var(_Ctx, _Var, _Value) ->
    {error, badarg}.

-spec register_fun(
    Ctx :: context(), PHPName :: binary(), 
    Module :: atom(), Fun :: atom()) -> ok | {error, reason()}.

register_fun(Ctx, _PHPName, _Module, _Fun) when not is_pid(Ctx) ->
    {error, enoctx};

register_fun(Ctx, PHPName, Module, Fun) ->
    ephp_context:register_func(Ctx, PHPName, Module, Fun).

-spec register_module(Ctx :: context(), Module :: atom()) -> ok.

register_module(Ctx, Module) ->
    Module:init(Ctx).

-spec eval(Context :: context(), PHP :: string() | binary()) -> 
    {ok, Result :: binary()} | {error, Reason :: reason()} | 
    {error,{Code::binary(), Line::integer(), Col::integer()}}.

eval(Context, PHP) ->
    case ephp_parser:parse(PHP) of
        {_,Code,{{line,Line},{column,Col}}} ->
            {error, {Code,Line,Col}};
        Compiled ->
            ephp_interpr:process(Context, Compiled)
    end.

-spec main(Args :: [string()]) -> integer().

main([Filename]) ->
    case file:read_file(Filename) of
    {ok, Content} ->
        {ok, Ctx} = context_new(),
        {ok, Result} = eval(Ctx, Content),
        io:format("~s", [Result]),
        0;
    {error, enoent} ->
        io:format("File not found: ~s~n", [Filename]),
        -2; 
    {error, Reason} ->
        io:format("Error: ~p~n", [Reason]),
        -3
    end;<commit_msg>add run and compile to the interface for compile once and run at any time<commit_after>-module(ephp).
-compile([warnings_as_errors]).

-export([
    context_new/0,
    register_var/3,
    register_fun/4,
    register_module/2,
    compile/1,
    run/2,
    eval/2,

    main/1   %% for escriptize
]).

-include("ephp.hrl").

-spec context_new() -> {ok, context()}.

context_new() ->
    Modules = ?MODULES,
    case ephp_context:start_link() of
        {ok, Ctx} -> 
            [ register_module(Ctx, Module) || Module <- Modules ],
            {ok, Ctx};
        Error ->
            Error
    end.

-type values() :: integer() | binary() | float().

-spec register_var(Ctx :: context(), Var :: binary(), Value :: values()) ->
    ok | {error, reason()}.

register_var(Ctx, _Var, _Value) when not is_pid(Ctx) ->
    {error, enoctx};

register_var(Ctx, Var, Value) when 
        is_integer(Value) orelse 
        is_float(Value) orelse
        is_binary(Value) orelse
        ?IS_DICT(Value) ->
    ephp_context:set(Ctx, #variable{name=Var}, Value);

register_var(_Ctx, _Var, _Value) ->
    {error, badarg}.

-spec register_fun(
    Ctx :: context(), PHPName :: binary(), 
    Module :: atom(), Fun :: atom()) -> ok | {error, reason()}.

register_fun(Ctx, _PHPName, _Module, _Fun) when not is_pid(Ctx) ->
    {error, enoctx};

register_fun(Ctx, PHPName, Module, Fun) ->
    ephp_context:register_func(Ctx, PHPName, Module, Fun).

-spec register_module(Ctx :: context(), Module :: atom()) -> ok.

register_module(Ctx, Module) ->
    Module:init(Ctx).

-spec compile(PHP :: binary() | string()) -> 
    {ok, Result :: [statement()]} | {error, Reason :: reason()} | 
    {error,{Code::binary(), Line::integer(), Col::integer()}}.

compile(PHP) ->
    case ephp_parser:parse(PHP) of
        {_,Code,{{line,Line},{column,Col}}} ->
            {error, {Code,Line,Col}};
        Compiled ->
            {ok, Compiled}
    end.

-spec run(Context :: context(), Compiled :: [statement()]) -> 
    {ok, binary()} | {error, Reason::reason()}.

run(Context, Compiled) ->
    ephp_interpr:process(Context, Compiled).

-spec eval(Context :: context(), PHP :: string() | binary()) -> 
    {ok, Result :: binary()} | {error, Reason :: reason()} | 
    {error,{Code::binary(), Line::integer(), Col::integer()}}.

eval(Context, PHP) ->
    case ephp_parser:parse(PHP) of
        {_,Code,{{line,Line},{column,Col}}} ->
            {error, {Code,Line,Col}};
        Compiled ->
            ephp_interpr:process(Context, Compiled)
    end.

-spec main(Args :: [string()]) -> integer().

main([Filename]) ->
    case file:read_file(Filename) of
    {ok, Content} ->
        {ok, Ctx} = context_new(),
        {ok, Result} = eval(Ctx, Content),
        io:format("~s", [Result]),
        0;
    {error, enoent} ->
        io:format("File not found: ~s~n", [Filename]),
        -2; 
    {error, Reason} ->
        io:format("Error: ~p~n", [Reason]),
        -3
    end;
{'fraction_non_alphanumeric': 0.19002284843869002, 'fraction_numerical': 0.003808073115003808, 'mean_word_length': 2.7955202312138727, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1010478', 'n_tokens_mistral': 1813, 'n_tokens_neox': 1812, 'n_words': 404}