% @doc experiment centering around the file syntax node using ntree approach -module(gsc_test_file). -export([ main/0 ]). -include("$gsc_include/gsc.hrl"). -record(ct, {payable = none :: none | false | {true, tk()}, main = none :: none | false | {true, tk()}, contract = none :: none | tk(), con = none :: none | tk(), impls = none :: none | [tk()], eq = none :: none | tk()}). -type meta() :: #ct{}. -record(decl_type, {type = none :: none | tk(), id = none :: none | tk(), params = none :: none | [tk()], eq = none :: none | tk()}). -type decl_meta() :: #decl_type{}. -type ast_meta() :: file | meta() | decl_meta() | nyi | {nyi, any()} . -type target() :: ct | iface | ns | pragma | include | using . -type s2t_target() :: file | top_decl | target() | nyi | {nyi, any()} . -type s2f_target() :: {block_of, s2t_target()} . -type ast() :: ntree(ast_meta(), tk()). -type asf() :: nforest(ast_meta(), tk()). main() -> HelloN = "hello.aes", HelloP = ts_utils:ct_file_abspath(HelloN), {ok, HelloS} = file:read_file(HelloP), S0 = gsc:unsafe_signal_from_file(HelloP), T1 = s2t(file, S0), io:format("hello.aes:~n", []), io:format("```~n", []), io:format("~ts", [HelloS]), io:format("```~n~n", []), io:format("AST: ~tp~n", [T1]), ok. % // Hello World Contract % // Copyright (c) 2025 QPQ AG % % contract Hello = % type state = unit % entrypoint init(): state = % () % % entrypoint hello(): string = % "hello, world" -spec s2t(ParseTarget, Signal) -> AST when ParseTarget :: file, Signal :: [tk()], AST :: ast(). % File ::= Block(TopDecl) s2t(file, Signal) -> case Signal of [] -> error(empty_file); _ -> {ns, file, s2f({block_of, top_decl}, Signal)} end; % TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl) % | ['payable'] 'contract' 'interface' Con [Implement] '=' Block(Decl) % | 'namespace' Con '=' Block(Decl) % | '@compiler' PragmaOp Version % | 'include' String % | Using s2t(top_decl, Signal) -> NewTarget = case gsc_tokens:strings(3, Signal) of ["payable", "contract", "interface"] -> iface; ["contract", "interface" | _] -> iface; ["payable", "main", "contract"] -> ct; ["payable", "contract" | _] -> ct; ["contract" | _] -> ct; ["namespace" | _] -> namespace; ["@compiler" | _] -> pragma; ["include" | _] -> include; ["using" | _] -> using end, s2t(NewTarget, Signal); % ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl) s2t(ct, S0) -> {slurp, CtMeta, S1} = s2s_slurp_meta(#ct{}, S0), {ns, CtMeta, s2f({block_of, decl}, S1)}; % Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias % | 'record' Id ['(' TVar* ')'] '=' RecordType % | 'datatype' Id ['(' TVar* ')'] '=' DataType % | 'let' Id [':' Type] '=' Expr % | (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl) % | Using s2t(decl, S0) -> NewTarget = case gsc_tokens:strings(3, S0) of ["type" | _] -> decl_type; ["record" | _] -> decl_record; ["datatype" | _] -> decl_datatype; ["let" | _] -> decl_let; Pfx3 -> IsEp = lists:member("entrypoint", Pfx3), IsFn = lists:member("function", Pfx3), if IsEp -> decl_entrypoint; IsFn -> decl_function; true -> error({bad_decl, S0}) end end, s2t(NewTarget, S0); % 'type' Id ['(' TVar* ')'] '=' TypeAlias s2t(decl_type, S0) -> {slurp, Meta, S1} = s2s_slurp_meta(#decl_type{}, S0), {ns, Meta, s2t(type, S1)}; s2t(nyi, Signal) -> {ns, nyi, Signal}; s2t(NYI = {nyi, _}, Signal) -> {ns, NYI, Signal}; s2t(NYI, Signal) -> {ns, {nyi, NYI}, Signal}. -spec s2f(ForestTarget, Signal) -> Forest when ForestTarget :: s2f_target(), Signal :: [tk()], Forest :: asf(). s2f({block_of, TreeTarget}, S0) -> {gulp, Items} = gsc_signal:gulp_block_items(S0), [s2t(TreeTarget, I) || I <- Items]. -spec s2s_slurp_meta(InitMeta, Signal) -> Result when InitMeta :: Meta, Signal :: [tk()], Result :: {slurp, Meta, NewSignal}, Meta :: ast_meta(), NewSignal :: Signal. s2s_slurp_meta(M = #ct{}, S) -> s2s_sm_ct(M, S); s2s_slurp_meta(M = #decl_type{}, S) -> s2s_sm_decl_type(M, S); s2s_slurp_meta(M, S) -> error({s2s_slurp_meta, M, S}). s2s_sm_ct(Ct = #ct{payable = none}, S0) -> case S0 of [#tk{str = "payable"} = T0 | S1] -> s2s_sm_ct(Ct#ct{payable = {true, T0}}, S1); _ -> s2s_sm_ct(Ct#ct{payable = false}, S0) end; s2s_sm_ct(Ct = #ct{main = none}, S0) -> case S0 of [#tk{str = "main"} = T0 | S1] -> s2s_sm_ct(Ct#ct{main = {true, T0}}, S1); _ -> s2s_sm_ct(Ct#ct{main = false}, S0) end; s2s_sm_ct(Ct = #ct{contract = none}, S0) -> case S0 of [#tk{str = "contract"} = T0 | S1] -> s2s_sm_ct(Ct#ct{contract = T0}, S1); _ -> error({no_kwd_contract, Ct, S0}) end; s2s_sm_ct(Ct = #ct{con = none}, S0) -> case S0 of [#tk{shape = con} = T0 | S1] -> s2s_sm_ct(Ct#ct{con = T0}, S1); _ -> error({no_contract_name, Ct, S0}) end; s2s_sm_ct(Ct = #ct{impls = none}, S0) -> case gsc_tokens:strings(1, S0) of [":"] -> {slurp, Impls, S1} = s2f_slurp_impls(S0), s2s_sm_ct(Ct#ct{impls = Impls}, S1); _ -> s2s_sm_ct(Ct#ct{impls = []}, S0) end; s2s_sm_ct(Ct = #ct{eq = none}, S0) -> case S0 of [#tk{str = "="} = T0 | S1] -> s2s_sm_ct(Ct#ct{eq = T0}, S1); _ -> error({no_equal_sign, Ct, S0}) end; s2s_sm_ct(Ct, S0) -> {slurp, Ct, S0}. s2f_slurp_impls([#tk{str = ":"}, #tk{shape = con} = I0 | S0]) -> s2f_slurp_impls([I0], S0). s2f_slurp_impls(Stk, [#tk{str = ","}, #tk{shape = con} = I0 | S0]) -> s2f_slurp_impls([I0 | Stk], S0); s2f_slurp_impls(Stk, S0) -> {slurp, lists:reverse(Stk), S0}. %-record(decl_type, % {type = none :: none | tk(), % id = none :: none | tk(), % params = none :: none | [tk()], % eq = none :: none | tk()}). s2s_sm_decl_type(M = #decl_type{type = none}, S0) -> case S0 of [#tk{str = "type"} = T0 | S1] -> s2s_sm_decl_type(M#decl_type{type = T0}, S1); _ -> error({no_kwd_type, S0}) end; s2s_sm_decl_type(M = #decl_type{id = none}, S0) -> case S0 of [#tk{shape = id} = T0 | S1] -> s2s_sm_decl_type(M#decl_type{id = T0}, S1); _ -> error({no_type_id, S0}) end; s2s_sm_decl_type(M = #decl_type{params = none}, S0) -> case S0 of [#tk{str = "("} = _T0 | _] -> error({fixme, parens_bad}); _ -> s2s_sm_decl_type(M#decl_type{params = []}, S0) end; s2s_sm_decl_type(M = #decl_type{eq = none}, S0) -> case S0 of [#tk{str = "="} = T0 | S1] -> s2s_sm_decl_type(M#decl_type{eq = T0}, S1); _ -> error({no_equal_sign, S0}) end; s2s_sm_decl_type(M, S0) -> {slurp, M, S0}.