Files
gsc/test/gsc_test_file.erl
T
2026-06-05 18:33:18 -07:00

274 lines
7.8 KiB
Erlang

% @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}.