274 lines
7.8 KiB
Erlang
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}.
|