Files
sfc/test/gsc_test_ytree.erl
T
Peter Harpending 2e062bb8c9 gsc_test_ytree: task now fails successfully
i have realized that a lot of the complexity and cognitive overhead
is downstream from trying to prematurely handle errors.

so i'm going to do the erlang thing and just assert/assume everything
worked perfectly and let the errors speak for themselves.

so re "task fails successfully": now instead of getting an NYI that
looks like a success result, we're now properly getting an error.
2026-08-12 00:02:44 -06:00

195 lines
6.4 KiB
Erlang

-module(gsc_test_ytree).
-export([main/0]).
-include("$gsc_include/gsc.hrl").
main() ->
Src =
"""
contract Foo =
type foo = bar
""",
handle_src_str(Src).
-type syntax_target() :: signal
| file
| {block_of, syntax_target()}
| top_decl
| contract
| interface
| namespace
| '@compiler'
| include
| using
.
-record(ct, {payable = none :: none | false | true,
main = none :: none | false | true,
contract = none :: none | tk(),
con = none :: none | tk(),
impls = none :: none | false | {true, [tk()]},
'=' = none :: none | tk()}).
%% @doc This is all the stuff needed to be consumed before parsin
%% anything in the contract
-type contract_meta() :: #ct{}.
%% @doc
%% This is all the stuff that has to be parsed before actual parsing
%% can begin.
-type stem_meta() :: none
| contract_meta().
% @doc ys = syntax stem
-record(ys, {target = signal :: syntax_target(),
signal :: [tk()],
stem_meta :: stem_meta(),
forest :: [#ys{}]}).
-type syntax_stem() :: #ys{}.
handle_src_str(SrcStr) ->
Sig = gsc:unsafe_signal_from_string(SrcStr),
Result = gulp(file, Sig),
io:format("~tp~n", [Result]).
-spec gulp(Target, Signal) -> Result when
Target :: syntax_target(),
Signal :: [tk()],
Result :: {gulp, syntax_stem()} | {error, any()}.
gulp(file, Sig) ->
gulp({block_of, top_decl}, Sig);
gulp({block_of, X}, Sig) ->
{gulp, BlockItems} = gsc_signal:gulp_block_items(Sig),
#ys{target = {block_of, X},
signal = Sig,
stem_meta = none,
forest = [gulp(X, Item) || Item <- BlockItems]};
gulp(top_decl, Sig) ->
% TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
% | 'contract' 'interface' Con [Implement] '=' Block(Decl)
% | 'namespace' Con '=' Block(Decl)
% | '@compiler' PragmaOp Version
% | 'include' String
% | Using
NewTarget =
case gsc_tokens:strings(3, Sig) of
["namespace", _, _] -> namespace;
["@compiler", _, _] -> '@compiler';
["include", _, _] -> include;
["using", _, _] -> using;
["payable", "main", "contract"] -> contract;
["payable", "contract", "interface"] -> interface;
["payable", "contract", _] -> contract;
["contract", "interface", _] -> interface;
["contract", _, _] -> contract;
["main", "contract", _] -> contract;
["main", "payable", "contract"] -> contract
end,
gulp(NewTarget, Sig);
gulp(contract, Sig) ->
{slurp, CtMeta, BlockSignal} = slurp(#ct{}, Sig),
{gulp, BlockItems} = gsc_signal:gulp_block_items(BlockSignal),
Forest = [gulp(decl, Item) || Item <- BlockItems],
#ys{target = contract,
signal = Sig,
stem_meta = CtMeta,
forest = Forest};
% Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
% | 'record' Id ['(' TVar* ')'] '=' RecordType
% | 'datatype' Id ['(' TVar* ')'] '=' DataType
% | 'let' Id [':' Type] '=' Expr
% | (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
% | Using
gulp(decl, Sig) ->
NewTarget =
case strings(1, Sig) of
["type"] -> type;
["record"] -> record;
["datatype"] -> datatype;
["let"] -> 'let';
["using"] -> using;
_ -> fn
end,
gulp(NewTarget, Sig);
gulp(NyiTarget, Sig) ->
error({nyi, [?MODULE, gulp, NyiTarget, Sig]}).
%% ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
slurp(Meta = #ct{payable = none, main = none}, Sig) ->
%% nato phonetic alphabet
{Pa, Mi, No} =
case gsc_tokens:strings(2, Sig) of
["payable", "main"] -> {true, true, 2};
["main", "payable"] -> {true, true, 2};
["payable", _] -> {true, false, 1};
["main", _] -> {false, true, 1};
_ -> {false, false, 0}
end,
NewMeta = Meta#ct{payable = Pa, main = Mi},
NewSig = take(No, Sig),
slurp(NewMeta, NewSig);
slurp(M = #ct{contract = none}, Sig) ->
case Sig of
[Tk = #tk{str = "contract"} | Rest] ->
slurp(M#ct{contract = Tk}, Rest);
_ ->
error(no_keyword_contract)
end;
slurp(M = #ct{con = none}, Sig) ->
case Sig of
[Tk = #tk{shape = con} | Rest] ->
slurp(M#ct{con = Tk}, Rest);
[#tk{str = Irregar} | _] ->
error(illegal_contract_name, Irregar);
[] ->
error(no_contract_name)
end;
slurp(M = #ct{impls = none}, Sig) ->
case Sig of
[#tk{str = ":"} | Rest] ->
{slurp, Impls, NewRest} = slurp({sep1, con, ","}, Rest),
NewM = M#ct{impls = {true, Impls}},
slurp(NewM, NewRest);
_ ->
slurp(M#ct{impls = false}, Sig)
end;
slurp(M = #ct{'=' = none}, Sig) ->
case Sig of
[Tk = #tk{str = "="} | Rest] ->
{slurp, M#ct{'=' = Tk}, Rest};
_ ->
error("where the fuck my = be at nigga")
end;
slurp({sep1, TkShape, SepByStr}, Tks) ->
case Tks of
[Tk = #tk{shape = TkShape} | Rest] ->
slurp_sep1_ii([Tk], TkShape, SepByStr, Rest);
_ ->
error(irregar)
end;
slurp(BadMeta, BadTokens) ->
error({nyi, [?MODULE, slurp, BadMeta, BadTokens]}).
slurp_sep1_ii(Stk, TkShape, SepByStr, Sig) ->
case Sig of
[#tk{str = SepByStr}, T = #tk{shape = TkShape} | Rest] ->
slurp_sep1_ii([T | Stk], TkShape, SepByStr, Rest);
_ ->
{slurp, lists:reverse(Stk), Sig}
end.
strings(N, Tks) ->
gsc_tokens:strings(N, Tks).
take(N, [_ | Rest]) when N >= 1 -> take(N-1, Rest);
take(0, List) -> List;
take(_, []) -> [].