more mass renaming

This commit is contained in:
Peter Harpending
2026-06-02 01:48:05 -07:00
parent eff77fff6b
commit 270f192f0c
53 changed files with 1264 additions and 431 deletions
+1 -1
View File
@@ -70,7 +70,7 @@
% `contract` gets tokenized as a keyword and not a variable name), and then
% calls into this module in order to match the string shape it's looking for.
% @end
-module(gsc_strmatch).
-module(gs_strmatch).
%-compile([export_all, nowarn_export_all]).
+124 -112
View File
@@ -16,11 +16,11 @@
% 2. to future-proof in case we decide to incrementally incorporate the gsc
% code into the legacy sophia compiler
% @end
-module(gsc_tokens).
-module(gs_tokens).
% meta
-export([
token_types_parse_order/0,
token_shapes_parse_order/0,
kwds/0
]).
@@ -39,10 +39,11 @@
is_significant/1,
filter_significant/1,
significant_tokens/1,
tokens_from_iolist/1,
tokens/1,
slurp_token/2,
slurp_token_types/3,
slurp_token_of_type/3,
slurp_token_shapes/3,
slurp_token_of_shape/3,
new_pos/2
]).
@@ -59,7 +60,7 @@
AtMostNStrings :: [string()].
% @doc return the strings of the first N tokens
strings(N, [#tk{string = S} | Rest]) when is_integer(N), N >= 1 ->
strings(N, [#tk{str = S} | Rest]) when is_integer(N), N >= 1 ->
[S | strings(N-1, Rest)];
strings(_, []) ->
[];
@@ -159,7 +160,7 @@ take_block_item([]) ->
% counterintuitive to end-users (who are programmers, entirely
% unfamiliar with notions like stacks and open/close delimiters)
slurp_plist([Hd = #tk{string = "("} | Tl]) ->
slurp_plist([Hd = #tk{str = "("} | Tl]) ->
slurp_dlist([Hd], [Hd], Tl);
slurp_plist(Tks) ->
{slurp, [], Tks}.
@@ -170,30 +171,30 @@ slurp_dlist(All, [], NewTokens) ->
{slurp, lists:reverse(All), NewTokens};
% WMA stack is nonempty
% happy cases of opens getting popped
slurp_dlist(All, [#tk{string = "("} | NewOpen],
[#tk{string = ")"} = Tk | NewTks]) ->
slurp_dlist(All, [#tk{str = "("} | NewOpen],
[#tk{str = ")"} = Tk | NewTks]) ->
slurp_dlist([Tk | All], NewOpen, NewTks);
slurp_dlist(All, [#tk{string = "["} | NewOpen],
[#tk{string = "]"} = Tk | NewTks]) ->
slurp_dlist(All, [#tk{str = "["} | NewOpen],
[#tk{str = "]"} = Tk | NewTks]) ->
slurp_dlist([Tk | All], NewOpen, NewTks);
slurp_dlist(All, [#tk{string = "{"} | NewOpen],
[#tk{string = "}"} = Tk | NewTks]) ->
slurp_dlist(All, [#tk{str = "{"} | NewOpen],
[#tk{str = "}"} = Tk | NewTks]) ->
slurp_dlist([Tk | All], NewOpen, NewTks);
% happy: open delimiters getting pushed
slurp_dlist(All, Opens, [#tk{string = "("} = Tk | NewTks]) ->
slurp_dlist(All, Opens, [#tk{str = "("} = Tk | NewTks]) ->
slurp_dlist([Tk | All], [Tk | Opens], NewTks);
slurp_dlist(All, Opens, [#tk{string = "["} = Tk | NewTks]) ->
slurp_dlist(All, Opens, [#tk{str = "["} = Tk | NewTks]) ->
slurp_dlist([Tk | All], [Tk | Opens], NewTks);
slurp_dlist(All, Opens, [#tk{string = "{"} = Tk | NewTks]) ->
slurp_dlist(All, Opens, [#tk{str = "{"} = Tk | NewTks]) ->
slurp_dlist([Tk | All], [Tk | Opens], NewTks);
% sad: mismatch cases
slurp_dlist(All, Opens, []) ->
{error, {fixme, mismatch, Opens, none}};
slurp_dlist(All, Opens, [#tk{string = "}"} = BadClose | _]) ->
slurp_dlist(All, Opens, [#tk{str = "}"} = BadClose | _]) ->
{error, {fixme, mismatch, Opens, {value, BadClose}}};
slurp_dlist(All, Opens, [#tk{string = "]"} = BadClose | _]) ->
slurp_dlist(All, Opens, [#tk{str = "]"} = BadClose | _]) ->
{error, {fixme, mismatch, Opens, {value, BadClose}}};
slurp_dlist(All, Opens, [#tk{string = ")"} = BadClose | _]) ->
slurp_dlist(All, Opens, [#tk{str = ")"} = BadClose | _]) ->
{error, {fixme, mismatch, Opens, {value, BadClose}}};
% general case: non-terminal token gets pushed
slurp_dlist(All, Opens, [Tk | NewTks]) ->
@@ -206,15 +207,15 @@ slurp_dlist(All, Opens, [Tk | NewTks]) ->
% This is parse order definition, list of keywords, etc
%
% -export([
% token_types_parse_order/0,
% token_shapes_parse_order/0,
% kwds/0
% ]).
%-------------------------------------------------------
-spec token_types_parse_order() -> [gsc_token_type()].
-spec token_shapes_parse_order() -> [tk_shape()].
% @doc
% list of sophia tokens in parse order (if an earlier type matches, the later
% type isn't even checked)
% list of sophia token shapes in parse order (if an earlier shape matches, the later
% shape isn't even checked)
%
%
% Rules =
@@ -245,7 +246,7 @@ slurp_dlist(All, Opens, [Tk | NewTks]) ->
% ],
% @end
token_types_parse_order() ->
token_shapes_parse_order() ->
% written in this style to be maximally editable
lists:flatten([
% comments and whitespace
@@ -282,8 +283,8 @@ kwds() ->
% -export([
% tokens/1,
% slurp_token/1,
% slurp_token_types/2,
% slurp_token_of_type/2
% slurp_token_shapes/2,
% slurp_token_of_shape/2
% ]).
%-------------------------------------------------------
@@ -323,12 +324,23 @@ filter_significant(Tokens) ->
-spec is_significant(Token) -> boolean()
when Token :: tk().
is_significant(#tk{type = bcom}) -> false;
is_significant(#tk{type = lcom}) -> false;
is_significant(#tk{type = ws}) -> false;
is_significant(#tk{shape = bcom}) -> false;
is_significant(#tk{shape = lcom}) -> false;
is_significant(#tk{shape = ws}) -> false;
is_significant(_) -> true.
-spec tokens_from_iolist(SrcStr) -> Result when
SrcStr :: iolist(),
Result :: {ok, Tokens}
| {error, gsc_err()},
Tokens :: [tk()].
% @doc alias for tokens/1
tokens_from_iolist(S) -> tokens(S).
-spec tokens(SrcStr) -> Result
when SrcStr :: iolist(),
Result :: {ok, Tokens}
@@ -349,7 +361,7 @@ tokens(Stack, _FinalPos, "") ->
{ok, lists:reverse(Stack)};
tokens(Stack, Pos, SrcStr) ->
case slurp_token(Pos, SrcStr) of
{tokmatch, NewToken = #tk{string = TokStr},
{tokmatch, NewToken = #tk{str = TokStr},
NewSrcStr} ->
NewPos = new_pos(Pos, TokStr),
tokens([NewToken | Stack], NewPos, NewSrcStr);
@@ -455,7 +467,7 @@ next_tabstop8(Col0) when Col0 >= 0 ->
-spec slurp_token(Pos, SrcStr) -> Result
when Pos :: gsc_pos(),
when Pos :: tk_pos(),
SrcStr :: string(),
Result :: {tokmatch, Token, Rest}
| no_tokmatch
@@ -465,17 +477,17 @@ next_tabstop8(Col0) when Col0 >= 0 ->
Rest :: string().
% @doc
% grab a single token off the front of the string according to
% `token_types_parse_order/0'
% `token_shapes_parse_order/0'
slurp_token(Pos, SrcStr) ->
% this is the easiest format if i need to fuck with it
slurp_token_types(token_types_parse_order(), Pos, SrcStr).
slurp_token_shapes(token_shapes_parse_order(), Pos, SrcStr).
-spec slurp_token_types(ParseOrder, Pos, SrcStr) -> Result
when ParseOrder :: [gsc_token_type()],
Pos :: gsc_pos(),
-spec slurp_token_shapes(ParseOrder, Pos, SrcStr) -> Result
when ParseOrder :: [tk_shape()],
Pos :: tk_pos(),
SrcStr :: string(),
Result :: {tokmatch, Token, Rest}
| no_tokmatch
@@ -485,22 +497,22 @@ slurp_token(Pos, SrcStr) ->
Rest :: string().
% @doc
% grab a single token off the front of the string according to
% `token_types_parse_order/0'
% `token_shapes_parse_order/0'
slurp_token_types([TokenType | TTs], Pos, SrcStr) ->
case slurp_token_of_type(TokenType, Pos, SrcStr) of
slurp_token_shapes([TokenType | TTs], Pos, SrcStr) ->
case slurp_token_of_shape(TokenType, Pos, SrcStr) of
Match = {tokmatch, _, _} -> Match;
no_tokmatch -> slurp_token_types(TTs, Pos, SrcStr);
no_tokmatch -> slurp_token_shapes(TTs, Pos, SrcStr);
IErr = {ierr, _} -> IErr;
Error = {error, _} -> Error
end;
slurp_token_types([], _Pos, _SrcStr) ->
slurp_token_shapes([], _Pos, _SrcStr) ->
no_tokmatch.
-spec slurp_token_of_type(TokenType, Pos, SrcStr) -> MaybeToken
when TokenType :: gsc_token_type(),
Pos :: gsc_pos(),
-spec slurp_token_of_shape(TokenType, Pos, SrcStr) -> MaybeToken
when TokenType :: tk_shape(),
Pos :: tk_pos(),
SrcStr :: string(),
MaybeToken :: {tokmatch, Token, Rest}
| no_tokmatch
@@ -509,7 +521,7 @@ slurp_token_types([], _Pos, _SrcStr) ->
Token :: tk(),
Rest :: string().
% @doc
% match a sophia token of a given type off the front of the string
% match a sophia token of a given shape off the front of the string
% @end
% COMMENTS AND WHITESPACE: lcom, bcom, ws
@@ -518,27 +530,27 @@ slurp_token_types([], _Pos, _SrcStr) ->
%
% i am not going to bother writing a string matcher thing for this
% FIXME: make a string matcher for line comments
slurp_token_of_type(lcom, Pos, SrcStr) ->
slurp_token_of_shape(lcom, Pos, SrcStr) ->
case SrcStr of
"//" ++ _ ->
{Line, Rest} = takeline("", SrcStr),
Token = #tk{type = lcom,
Token = #tk{shape = lcom,
pos = Pos,
string = Line},
str = Line},
{tokmatch, Token, Rest};
_ ->
no_tokmatch
end;
% Block comments cannot have a string matcher because they have a whole stack
% thing keeping track of depth because of nested block comments
slurp_token_of_type(bcom, Pos, SrcStr0) ->
slurp_token_of_shape(bcom, Pos, SrcStr0) ->
case SrcStr0 of
"/*" ++ SrcStr1 ->
case bcom("/*", 1, SrcStr1) of
{ok, CommentStr, SrcStr2} ->
Token = #tk{type = bcom,
Token = #tk{shape = bcom,
pos = Pos,
string = CommentStr},
str = CommentStr},
{tokmatch, Token, SrcStr2};
Error ->
Error
@@ -546,15 +558,15 @@ slurp_token_of_type(bcom, Pos, SrcStr0) ->
_ ->
no_tokmatch
end;
slurp_token_of_type(ws, Pos, SrcStr) ->
WhitespaceMatcher = gsc_strmatch:smr_sf_ws(),
case gsc_strmatch:match(WhitespaceMatcher, SrcStr) of
slurp_token_of_shape(ws, Pos, SrcStr) ->
WhitespaceMatcher = gs_strmatch:smr_sf_ws(),
case gs_strmatch:match(WhitespaceMatcher, SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, WS, Rest} ->
Token = #tk{type = ws,
Token = #tk{shape = ws,
pos = Pos,
string = WS},
str = WS},
{tokmatch, Token, Rest}
end;
% KEYWORDS, OPERATORS, PUNCTUATION: kwd, op, punct
@@ -568,88 +580,88 @@ slurp_token_of_type(ws, Pos, SrcStr) ->
%
% we know kwds are always ids, so we parse it as an id and see if it's one
% of the kwds
slurp_token_of_type(kwd, Pos, SrcStr) ->
case slurp_token_of_type(id, Pos, SrcStr) of
{tokmatch, IdTok = #tk{string = IdStr}, Rest} ->
slurp_token_of_shape(kwd, Pos, SrcStr) ->
case slurp_token_of_shape(id, Pos, SrcStr) of
{tokmatch, IdTok = #tk{str = IdStr}, Rest} ->
case lists:member(IdStr, kwds()) of
false ->
no_tokmatch;
true ->
KwTok = IdTok#tk{type = kwd},
KwTok = IdTok#tk{shape = kwd},
{tokmatch, KwTok, Rest}
end;
no_tokmatch ->
no_tokmatch
end;
slurp_token_of_type(op, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_op(), SrcStr) of
slurp_token_of_shape(op, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_op(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = op, pos = Pos, string = Str},
Token = #tk{shape = op, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(punct, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_punct(), SrcStr) of
slurp_token_of_shape(punct, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_punct(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = punct, pos = Pos, string = Str},
Token = #tk{shape = punct, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
% SOPHIA VARIABLE NAMES: id, con, qid, qcon, tvar
slurp_token_of_type(id, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_id(), SrcStr) of
slurp_token_of_shape(id, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_id(), SrcStr) of
{strmatch, IdStr, Rest} ->
Token = #tk{type = id, pos = Pos, string = IdStr},
Token = #tk{shape = id, pos = Pos, str = IdStr},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(con, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_con(), SrcStr) of
slurp_token_of_shape(con, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_con(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = con, pos = Pos, string = Str},
Token = #tk{shape = con, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(qid, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_qid(), SrcStr) of
slurp_token_of_shape(qid, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_qid(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = qid, pos = Pos, string = Str},
Token = #tk{shape = qid, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(qcon, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_qcon(), SrcStr) of
slurp_token_of_shape(qcon, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_qcon(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = qcon, pos = Pos, string = Str},
Token = #tk{shape = qcon, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(tvar, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_tvar(), SrcStr) of
slurp_token_of_shape(tvar, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_tvar(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = tvar, pos = Pos, string = Str},
Token = #tk{shape = tvar, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(int16, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_int16(), SrcStr) of
slurp_token_of_shape(int16, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_int16(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = int16, pos = Pos, string = Str},
Token = #tk{shape = int16, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
end;
slurp_token_of_type(int10, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_int10(), SrcStr) of
slurp_token_of_shape(int10, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_int10(), SrcStr) of
{strmatch, Str, Rest} ->
Token = #tk{type = int10, pos = Pos, string = Str},
Token = #tk{shape = int10, pos = Pos, str = Str},
{tokmatch, Token, Rest};
no_strmatch ->
no_tokmatch
@@ -658,63 +670,63 @@ slurp_token_of_type(int10, Pos, SrcStr) ->
% ak, ct, sg
%
% char: sophia char literal
slurp_token_of_type(ak, Pos, SrcStr) ->
StringMatcher = gsc_strmatch:smr_sf_ak(),
case gsc_strmatch:match(StringMatcher, SrcStr) of
slurp_token_of_shape(ak, Pos, SrcStr) ->
StringMatcher = gs_strmatch:smr_sf_ak(),
case gs_strmatch:match(StringMatcher, SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = ak, pos = Pos, string = TokenStr},
Token = #tk{shape = ak, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(ct, Pos, SrcStr) ->
StringMatcher = gsc_strmatch:smr_sf_ct(),
case gsc_strmatch:match(StringMatcher, SrcStr) of
slurp_token_of_shape(ct, Pos, SrcStr) ->
StringMatcher = gs_strmatch:smr_sf_ct(),
case gs_strmatch:match(StringMatcher, SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = ct, pos = Pos, string = TokenStr},
Token = #tk{shape = ct, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(sg, Pos, SrcStr) ->
StringMatcher = gsc_strmatch:smr_sf_sg(),
case gsc_strmatch:match(StringMatcher, SrcStr) of
slurp_token_of_shape(sg, Pos, SrcStr) ->
StringMatcher = gs_strmatch:smr_sf_sg(),
case gs_strmatch:match(StringMatcher, SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = sg, pos = Pos, string = TokenStr},
Token = #tk{shape = sg, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(char, Pos, SrcStr) ->
StringMatcher = gsc_strmatch:smr_sf_char(),
case gsc_strmatch:match(StringMatcher, SrcStr) of
slurp_token_of_shape(char, Pos, SrcStr) ->
StringMatcher = gs_strmatch:smr_sf_char(),
case gs_strmatch:match(StringMatcher, SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = char, pos = Pos, string = TokenStr},
Token = #tk{shape = char, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(string, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_str(), SrcStr) of
slurp_token_of_shape(string, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_str(), SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = string, pos = Pos, string = TokenStr},
Token = #tk{shape = string, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(bytes, Pos, SrcStr) ->
case gsc_strmatch:match(gsc_strmatch:smr_sf_bytes(), SrcStr) of
slurp_token_of_shape(bytes, Pos, SrcStr) ->
case gs_strmatch:match(gs_strmatch:smr_sf_bytes(), SrcStr) of
no_strmatch ->
no_tokmatch;
{strmatch, TokenStr, Rest} ->
Token = #tk{type = bytes, pos = Pos, string = TokenStr},
Token = #tk{shape = bytes, pos = Pos, str = TokenStr},
{tokmatch, Token, Rest}
end;
slurp_token_of_type(NyiType, Pos, SrcStr) ->
Message = io_lib:format("cannot slurp token of type: ~p", [NyiType]),
slurp_token_of_shape(NyiType, Pos, SrcStr) ->
Message = io_lib:format("cannot slurp token of shape: ~p", [NyiType]),
error(#gsc_err{atom = nyi,
string = Message,
extra = [{token_type, NyiType},
str = Message,
extra = [{token_shape, NyiType},
{pos, Pos},
{rest, SrcStr}]}).
+7 -59
View File
@@ -4,15 +4,14 @@
% based on original sophia compiler
%
% parse layers:
% 1. gsc_tokenizer: SrcStr -> (Tokens | SigTokens)
% 1. gs_tokens: SrcStr -> (Tokens | SigTokens)
%
% SigTokens = not comment/whitespace
%
% layers:
% a. gsc_strmatch : matches string shapes
% b. gsc_so_scan : converts to so_scan shapes
% a. gs_strmatch : matches string shapes
% b. gso_scan : converts to so_scan shapes
%
% 2. gsc_ast: SigTokens -> AST
%
% terminology:
%
@@ -30,16 +29,8 @@
%
% @end
% TODO:
% - barf for outputs, slurp for inputs
% - architecture needs more careful thought but only after something works
% - too fuzzy right now
% - possibly:
% - rename parser layers sequentially:
% - gsc_
-module(gsc).
-export_type([
token/0
]).
@@ -48,10 +39,7 @@
sigtokens_from_file/1,
sigtokens_from_string/1,
tokens_from_file/1,
tokens_from_string/1,
ast_from_file/1,
ast_from_string/1,
ast_from_tokens/1
tokens_from_string/1
]).
-include("$gsc_include/gsc.hrl").
@@ -68,13 +56,13 @@
sigtokens_from_file(X) ->
case tokens_from_file(X) of
{ok, Y} -> {ok, gsc_tokens:filter_significant(Y)};
{ok, Y} -> {ok, gs_tokens:filter_significant(Y)};
Err -> Err
end.
sigtokens_from_string(X) ->
case tokens_from_string(X) of
{ok, Y} -> {ok, gsc_tokens:filter_significant(Y)};
{ok, Y} -> {ok, gs_tokens:filter_significant(Y)};
Err -> Err
end.
@@ -101,44 +89,4 @@ tokens_from_file(FilePath) ->
Tokens :: [tk()].
tokens_from_string(SrcStr) ->
gsc_tokens:tokens(SrcStr).
-spec ast_from_file(FilePath) -> Perhaps
when FilePath :: string(),
Perhaps :: {ok, AST} | {error, gsc_err()},
AST :: gsc_ast().
ast_from_file(FilePath) ->
case file:read_file(FilePath) of
{ok, FileBytes} -> ast_from_string(FileBytes);
Error -> Error
end.
-spec ast_from_string(SrcStr) -> Perhaps
when SrcStr :: string(),
Perhaps :: {ok, AST} | {error, gsc_err()},
AST :: gsc_ast().
ast_from_string(SrcStr) ->
case gsc_tokens:significant_tokens(SrcStr) of
{ok, SigTks} -> ast_from_tokens(SigTks);
Error -> Error
end.
-spec ast_from_tokens(SrcTokens) -> Perhaps
when SrcTokens :: [tk()],
Perhaps :: {ok, AST} | {error, gsc_err()},
AST :: gsc_ast().
ast_from_tokens(Tks) ->
SigTks = gsc_tokens:filter_significant(Tks),
case gsc_ast:gulp_file(SigTks) of
{gulp, AST} -> {ok, AST};
Error -> Error
end.
gs_tokens:tokens(SrcStr).
-693
View File
@@ -1,693 +0,0 @@
% @doc
% type for a GSC AST
%
% based on so_syntax.erl
%
% this is the second layer in the parsing pipeline after tokens. From
% so_syntax:
%
% ## Declarations
%
% A Sophia file consists of a sequence of *declarations* in a layout block.
%
% ```c
% File ::= Block(TopDecl)
%
% TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
% | 'contract' 'interface' Con [Implement] '=' Block(Decl)
% | 'namespace' Con '=' Block(Decl)
% | '@compiler' PragmaOp Version
% | 'include' String
% | Using
%
% Implement ::= ':' Sep1(Con, ',')
%
% Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
% | 'record' Id ['(' TVar* ')'] '=' RecordType
% | 'datatype' Id ['(' TVar* ')'] '=' DataType
% | 'let' Id [':' Type] '=' Expr
% | (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
% | Using
%
% FunDecl ::= Id ':' Type // Type signature
% | Id Args [':' Type] '=' Block(Stmt) // Definition
% | Id Args [':' Type] Block(GuardedDef) // Guarded definitions
%
% GuardedDef ::= '|' Sep1(Expr, ',') '=' Block(Stmt)
%
% Using ::= 'using' Con ['as' Con] [UsingParts]
% UsingParts ::= 'for' '[' Sep1(Id, ',') ']'
% | 'hiding' '[' Sep1(Id, ',') ']'
%
% PragmaOp ::= '<' | '=<' | '==' | '>=' | '>'
% Version ::= Sep1(Int, '.')
%
% EModifier ::= 'payable' | 'stateful'
% FModifier ::= 'stateful' | 'private'
%
% Args ::= '(' Sep(Pattern, ',') ')'
%
% Type ::= Domain '=>' Type // Function type
% | Type '(' Sep(Type, ',') ')' // Type application
% | '(' Type ')' // Parens
% | 'unit' | Sep(Type, '*') // Tuples
% | Id | QId | TVar
%
% Domain ::= Type // Single argument
% | '(' Sep(Type, ',') ')' // Multiple arguments
% ```
-module(gsc_ast).
%-compile([export_all,nowarn_export_all]).
%
%%-export_type([
%%]).
%%
%%-export([
%% slurp/2
%%]).
%
%-export([
% gulp_file/1
%]).
%
%-include("$gsc_include/gsc.hrl").
%
%%%-----------------------------
%%% TYPES: gsc_ast
%%%-----------------------------
%
%
%% % placeholders
%-type ast_() :: any().
%-record(ast_nyi, {tokens = none :: [tk()]}).
%-type ast_nyi() :: #ast_nyi{}.
%
%
%% product type: foo * bar * baz
%%
%% stupid weird implication from bad syntax foresight trying to be
%% fancy and overload what parens do is products must always have at
%% least two operands; probably this is because `(foo)` is always the
%% same as `foo`
%%
%% - 0-tuple -> `unit`
%% - 1-tuple -> type itself
%% - 2+ -> here
%-record(ast_te_pi,
% {types = none :: none | [ast_te_()]}).
%
%-type ast_type_expr()
% :: #ast_te_fn{} % function (string, string) => string
% | #ast_te_ap{} % application map(string, int)
% | #ast_te_pi{} % product foo * bar * baz
% | #ast_te_tk{} % token string int 'a Foo.Bar.baz
% | ast_nyi().
%-type ast_te() :: ast_type_expr().
%
%
%% ta = type alias
%% 'type' Id ['(' TVar* ')'] '=' TypeAlias
%-record(ast_ta,
% {alias = none :: none | tk(),
% tvars = none :: none | [tk()],
% points_to = none :: none | ast_type_expr()}).
%
%% Decl ::= 'type' Id ['(' TVar* ')'] '=' TypeAlias
%% | 'record' Id ['(' TVar* ')'] '=' RecordType
%% | 'datatype' Id ['(' TVar* ')'] '=' DataType
%% | 'let' Id [':' Type] '=' Expr
%% | (EModifier* 'entrypoint' | FModifier* 'function') Block(FunDecl)
%% | Using
%-type decl() :: #ast_ta{}
% | #ast_nyi{}.
%
%% ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
%-record(ast_ct,
% {payable = none :: none | boolean(),
% main = none :: none | boolean(),
% name = none :: none | tk(),
% impls = none :: none | [tk()],
% decls = none :: none | [decl()]}).
%
%-record(ast_td_iface,{}).
%-record(ast_td_ns,{}).
%-record(ast_td_compiler,{}).
%-record(ast_td_include,{}).
%-record(ast_using,{}).
%
%% @doc
%% TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
%% | 'contract' 'interface' Con [Implement] '=' Block(Decl)
%% | 'namespace' Con '=' Block(Decl)
%% | '@compiler' PragmaOp Version
%% | 'include' String
%% | Using
%-type top_decl() :: #ast_ct{}
% | #ast_td_iface{}
% | #ast_td_ns{}
% | #ast_td_compiler{}
% | #ast_td_include{}
% | #ast_using{}
% .
%
%-record(ast_file,
% {top_decls :: [top_decl()]}).
%-type ast_file() :: #ast_file{}.
%
%
%-type parse_error_() :: any().
%-record(parse_error,
% {pos = none :: none | gsc_pos(),
% msg = "" :: string(),
% subs = [] :: [parse_error_()],
% extra = none :: any()}).
%-type parse_error() :: parse_error().
%
%
%-type ast()
% :: #ast_ct{}
% | #ast_file{}
% | #ast_nyi{}
% | #ast_ta{}
% | #ast_td_compiler{}
% | #ast_td_iface{}
% | #ast_td_include{}
% | #ast_td_ns{}
% | #ast_te_ap{} % application map(string, int)
% | #ast_te_fn{} % function (string, string) => string
% | #ast_te_pi{} % product foo * bar * baz
% | #ast_te_tk{} % token string int 'a Foo.Bar.baz
% | #ast_using{}.
%
%
%
%
%
%%%-----------------------------
%%% FUNCTIONS
%%%-----------------------------
%
%-spec gulp_file(Tokens) -> Perhaps
% when Tokens :: [tk()],
% Perhaps :: {gulp, #ast_file{}}
% | {error, #parse_error{}}.
%
%gulp_file([]) ->
% {error, empty_file};
%gulp_file(Tokens) ->
% case gsc_tokens:take_block(Tokens) of
% {Tokens, []} ->
% gulp_block(fun gulp_top_decl/1, Tokens);
% %gulp_file2([], [], Tokens);
% {A, B} ->
% StartPos = gsc_tokens:start_pos(A),
% ErrPos = gsc_tokens:start_pos(B),
% Msg = efmt("gulp_file: block starting at ~p ends at ~p instead of EOF",
% [StartPos, ErrPos]),
% {error, #parse_error{pos = ErrPos, msg = Msg}}
% end.
%
%
%
%%gulp_file2(AccOks, AccErrs, Tokens = [_ | _]) ->
%% % ItemTokens will be nonempty
%% {ItemTokens, NewTokens} = gsc_tokens:take_block_item(Tokens),
%% case gulp_top_decl(ItemTokens) of
%% {gulp, Ok} -> gulp_file2([Ok | AccOks], AccErrs, NewTokens);
%% Err -> gulp_file2(AccOks, [Err | AccErrs], NewTokens)
%% end;
%%gulp_file2(AccOks, [], []) ->
%% TopDecls = lists:reverse(AccOks),
%% {gulp, #ast_file{top_decls = TopDecls}};
%%gulp_file2(_, AccErrs, []) ->
%% Errs = [#parse_error{pos = Pos} | _]
%% = lists:reverse(AccErrs),
%% {error, #parse_error{pos = Pos, subs = Errs}}.
%
%-spec gulp_block(GulpItem, Tokens) -> GulpedItems
% when GulpItem :: fun((ItemTokens) -> GulpedItem),
% Tokens :: [tk()],
% ItemTokens :: Tokens,
% GulpedItem :: {gulp, Item} | {error, Reason},
% GulpedItems :: {gulp, Items} | {error, Reason},
% Items :: [Item],
% Item :: ast(),
% Reason :: any().
%% @doc
%% this almost is the familiar `traverse' idiom from Haskell
%%
%% ghci> :t traverse
%% traverse
%% :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
%% ghci> :t mapM
%% mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
%%
%% (or `mapM' if youre being sloppy)
%%
%% however in our case our function is actually of
%% signature
%%
%% :: (t a -> f b)
%%
%% so this is more sophisticated
%% @end
%
%gulp_block(GulpItem, Tokens) ->
% gulp_block(GulpItem, [], [], Tokens).
%
%gulp_block(GulpItem, AccOks, AccErrs, Tokens = [_ | _]) ->
% % ItemTokens will be nonempty
% {ItemTokens, NewTokens} = gsc_tokens:take_block_item(Tokens),
% case GulpItem(ItemTokens) of
% {gulp, Ok} -> gulp_block(GulpItem, [Ok | AccOks], AccErrs, NewTokens);
% Err -> gulp_block(GulpItem, AccOks, [Err | AccErrs], NewTokens)
% end;
%gulp_block(_, AccOks, [], []) ->
% Items = lists:reverse(AccOks),
% {gulp, Items};
%gulp_block(_, _, AccErrs, []) ->
% {error, #parse_error{subs = lists:reverse(AccErrs)}}.
%
%
%
%-spec gulp_top_decl(DeclTokens) -> Result
% when DeclTokens :: [tk()],
% Result :: {gulp, ast()} | {error, any()}.
%
%% @doc
%% TopDecl ::= ['payable'] ['main'] 'contract' Con [Implement] '=' Block(Decl)
%% | 'contract' 'interface' Con [Implement] '=' Block(Decl)
%% | 'namespace' Con '=' Block(Decl)
%% | '@compiler' PragmaOp Version
%% | 'include' String
%% | Using
%% @end
%gulp_top_decl(DeclTokens) ->
% case gsc_tokens:strings(3, DeclTokens) of
% ["payable", "contract", "interface"] ->
% gulp_nyi(DeclTokens);
% ["contract", "interface" | _] ->
% gulp_nyi(DeclTokens);
% ["payable", "main", "contract"] ->
% gulp_ct2(#ast_ct{payable = true, main = true},
% drop(3, DeclTokens));
% ["main", "contract" | _] ->
% gulp_ct2(#ast_ct{payable = false, main = true},
% drop(2, DeclTokens));
% ["payable", "contract" | _] ->
% gulp_ct2(#ast_ct{payable = true, main = false},
% drop(2, DeclTokens));
% ["contract" | _] ->
% gulp_ct2(#ast_ct{payable = false, main = false},
% drop(1, DeclTokens));
% ["namespace" | _] ->
% gulp_nyi(DeclTokens);
% ["@", "compiler" | _] ->
% gulp_nyi(DeclTokens);
% ["include" | _] ->
% gulp_nyi(DeclTokens);
% ["using" | _ ] ->
% gulp_nyi(DeclTokens);
% _ ->
% % decl tokens will always be nonempty
% [H = #tk{pos = ErrPos} | _] = DeclTokens,
% EMsg = efmt("gulp_top_decl: bad token: ~p; "
% "expecting one of: [payable, main, contract, namespace, @compiler, include, using]",
% [H]),
% {error, #parse_error{pos = ErrPos,
% msg = EMsg}}
% end.
%
%
%gulp_ct2(Ast, Tokens = [#tk{pos = Pos} | _]) ->
% % need to pass through pos for error messages on premature end of
% % input... for now it's a fixme thing
% gulp_ct3(Ast, Tokens, Pos).
%
%
%% payable main contract X : Y, Z, W = <block>
%% ^
%gulp_ct3(Ast = #ast_ct{name = none}, Tokens, Pos) ->
% NewPos = Pos, %% fixme
% case Tokens of
% [Name = #tk{type = con} | NewTokens] ->
% NewAst = Ast#ast_ct{name = Name},
% gulp_ct3(NewAst, NewTokens, NewPos);
% [Name | _] ->
% {error, {fixme, {bad_contract_name, Name}}};
% [] ->
% {error, {fixme, no_contract_name}}
% end;
%% contract X = <block>
%% contract X : Y, Z, W = <block>
%% ^
%gulp_ct3(Ast = #ast_ct{impls = none}, Tokens, Pos) ->
% NewPos = Pos, %% fixme
% case slurp_ct_impls(Tokens) of
% {slurp, Impls, NewTokens} ->
% NewAst = Ast#ast_ct{impls = Impls},
% gulp_ct3(NewAst, NewTokens, NewPos);
% Error ->
% Error
% end;
%% contract X = <block>
%% contract X : Y, Z, W = <block>
%% ^
%gulp_ct3(Ast = #ast_ct{decls = none},
% [#tk{string = "="} | Tokens],
% _Pos) ->
% case gulp_block(fun gulp_decl/1, Tokens) of
% {gulp, Decls} ->
% Result = Ast#ast_ct{decls = Decls},
% {gulp, Result};
% Error ->
% Error
% end;
%gulp_ct3(Ast, Tokens, Pos) ->
% {error, {fixme, {nyi, gulp_ct3, [Ast, Tokens, Pos]}}}.
%
%
%
%-spec slurp_ct_impls(Tokens) -> Slurped
% when Tokens :: [tk()],
% Slurped :: {slurp, Impls, NewTokens}
% | {error, Reason},
% Impls :: Tokens,
% NewTokens :: Tokens,
% Reason :: any().
%% @doc ": X, Y, Z = ..." ~> {slurp, [X, Y, Z], "= ..."}
%
%% FIXME: this should be a restructured a tiny bit for better error
%% handling
%slurp_ct_impls([_ = #tk{string = ":"},
% Con = #tk{type = con}
% | NewTokens]) ->
% slurp_ct_impls2([Con], NewTokens);
%slurp_ct_impls(NewTokens = [#tk{string = "="} | _]) ->
% {slurp, [], NewTokens};
%slurp_ct_impls([BadToken | _]) ->
% Msg = efmt("slurp_ct_impls: expecting (: Con | =), got ~p", [BadToken]),
% {error, {fixme, Msg}};
%slurp_ct_impls([]) ->
% {error, {fixme, "expecting : or =, got end-of-input"}}.
%
%
%slurp_ct_impls2(Stk, [_ = #tk{string = ","},
% Con = #tk{type = con}
% | NewTokens]) ->
% slurp_ct_impls2([Con | Stk], NewTokens);
%slurp_ct_impls2(Stk, NewTokens = [#tk{string = "="} | _]) ->
% {slurp, lists:reverse(Stk), NewTokens};
%slurp_ct_impls2(Stk, BadTokens) ->
% {error, {fixme, nyi, slurp_ct_impls2, [Stk, BadTokens]}}.
%
%
%% 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(Tokens) ->
% case gsc_tokens:strings(1, Tokens) of
% ["type"] -> gulp_type_alias(Tokens);
% _ -> gulp_nyi(Tokens)
% end.
%
%% 'type' Id ['(' TVar* ')'] '=' TypeAlias
%gulp_type_alias(Tokens) ->
% gulp_ta(#ast_ta{}, Tokens).
%
%
%% 'type' Id ['(' TVar* ')'] '=' TypeAlias
%% ^
%gulp_ta(Ast = #ast_ta{alias = none},
% _ = [#tk{string = "type"}, Alias = #tk{type = id}
% | NewTokens]) ->
% NewAst = Ast#ast_ta{alias = Alias},
% gulp_ta(NewAst, NewTokens);
%% 'type' Id ['(' TVar* ')'] '=' TypeAlias
%% ^
%gulp_ta(Ast = #ast_ta{tvars = none},
% Tokens) ->
% case slurp_ta_tvars(Tokens) of
% {slurp, TVars, NewTokens} ->
% NewAst = Ast#ast_ta{tvars = TVars},
% gulp_ta(NewAst, NewTokens);
% Error -> Error
% end;
%% 'type' Id ['(' TVar* ')'] '=' TypeAlias
%% ^
%gulp_ta(Ast = #ast_ta{points_to = none},
% _ = [#tk{string = "="} | NewTokens]) ->
% case gulp_type_expr(NewTokens) of
% {gulp, TypeExpr} ->
% Result = Ast#ast_ta{points_to = TypeExpr},
% {gulp, Result};
% Error -> Error
% end;
%gulp_ta(A, B) ->
% {error, {fixme, gulp_ta, A, B, ?MODULE, ?LINE}}.
%
%
%% type foo(bar, baz) = quux
%% ^
%%
%% "(bar, baz) = ..." ~> {slurp, [bar, baz], "= ..."}
%slurp_ta_tvars(Tks = [#tk{string = "="} | _]) ->
% {slurp, [], Tks};
%slurp_ta_tvars([#tk{string = "("},
% #tk{string = ")"}
% | NewTokens]) ->
% {slurp, [], NewTokens};
%slurp_ta_tvars([_ = #tk{string = "("},
% TVar = #tk{type = tvar}
% | NewTokens]) ->
% slurp_tavars([TVar], NewTokens).
%
%
%slurp_tavars(Stk,
% [_ = #tk{string = ","},
% TVar = #tk{type = tvar}
% | NewTks]) ->
% slurp_tavars([TVar | Stk], NewTks);
%slurp_tavars(Stk,
% [#tk{string = ")"}
% | NewTks]) ->
% {slurp, lists:reverse(Stk), NewTks}.
%
%
%gulp_type_expr(Tokens) ->
% case slurp_type_expr(Tokens) of
% {slurp, T, []} -> {gulp, T};
% {slurp, _, E} -> {error, {fixme, trailing, E}};
% Error -> Error
% end.
%
%
%% Type ::= Domain '=>' Type // Function type
%% | Type '(' Sep(Type, ',') ')' // Type application
%% | '(' Type ')' // Parens
%% | 'unit'
%% | Sep(Type, '*') // Tuples
%% | Id
%% | QId
%% | TVar
%%
%% Domain ::= Type // Single argument
%% | '(' Sep(Type, ',') ')' // Multiple arguments
%
%% terminals:
%% TkType = id | qid | tvar
%% PList = PList0 | PList1 | PList2
%% ProdType = Type [* Type]+
%% LamType = Type [=> Type]+
%%
%% PList0 = ()
%% PList1 = (Type)
%% PList2 = (Type [, Type]+)
%%
%% Ah ok:
%%
%% Type1 = TkType | PList(Type)
%%
%% Type = Type1 % string, (string), (string, string)
%% | Type1 => Type % function
%% | Type1 PList(Type) % application
%% | Type1 * Type % product
%
%
%% each type either starts with an id/qid/tvar token or an open paren
%%
%% we're going to eliminate open paren cases first:
%%
%% legal cases:
%%
%% 1. Function types can have arbitrary #args:
%%
%% () => ... // legal
%% (_) => ... // legal
%% (_, _) => ... // legal
%%
%% 2. Otherwise it must be a plist of ONLY one type
%%
%% () // illegal, should be "unit"
%% (X) // legal, equiv to X
%% (X, Y) // illegal, should be X * Y
%%
%slurp_type_expr(Tks = [#tk{pos = Pos} | _]) ->
% case slurp_type1(Tks) of
% % plist cases
% % function types _ => _
% {slurp, {plist, PList}, NewTks} ->
% case {PList, NewTks} of
% % ANY plist (including (), (foo)) followed by => is fine
% % () => _
% % (_) => _
% % (_, _) => _
% {DomainType, [#tk{string = "=>"} | After]} ->
% case slurp_type_expr(After) of
% {slurp, CodomainType, NewTks} ->
% TypeExpr = #ast_te_fn{dom = DomainType,
% codom = CodomainType},
% {slurp, TypeExpr, NewTks};
% Error = {error, _} ->
% Error
% end;
% % WMA not a function type, so only plists with 1 type are
% % allowed
% {[One], After} ->
% slurp_type_expr2(One, After);
% % plists of 0 or 2+ not allowed
% {[], _} ->
% Msg = "() is called unit",
% {error, {fixme, ?MODULE, ?LINE, illegal_type, Pos, Msg}};
% {_, _} ->
% Msg = "(_, _, ...) must be followed by =>; "
% "the type for tuples (A, B) is A * B",
% {error, {fixme, ?MODULE, ?LINE, illegal_type, Pos, Msg}}
% end;
% % first token is id|qid|tvar: foo, Foo.bar, 'foo
% {slurp, {token, Tk}, NewTks} ->
% case NewTks of
% % foo => bar is fine, normalized to (foo) => bar
% [#tk{string = "=>"} | After] ->
% case slurp_type_expr(After) of
% {slurp, Codom, NewAfter} ->
% ArgType = #ast_te_tk{token = ArgTypeTk},
% Dom = [ArgType],
% Result = #ast_te_fn{dom = Dom, codom = Codom},
% {slurp, Result, NewAfter};
% Error = {error, _} ->
% Error
% end;
% {_, _} ->
% One = #ast_te_tk{token = Tk},
% slurp_type_expr2(One, After)
% end;
% Error = {error, _} ->
% Error
% end.
%
%
%% parsed the first type, now need to see if this is an application
%% or a product or if we're done
%slurp_type_expr2(FirstType, After) ->
% % fixme: just going to ignore products and applications for now
% {slurp, FirstType, After}.
% %case strings(1, After) of
% % ["("] -> error(nyi);
% % ["*"] -> error(nyi);
% %case After of
% % [#tk{string = "("} | _] ->
% % case slurp_type1_II(After) of
% % {slurp, {plist, ArgTypes}, NewAfter} ->
% % {slurp, #ast_te_ap{fn = FirstType
%
%
%
%
%% Type1 = {plist, Types} () (foo) (foo, bar)
%% | {token, #tk{}} foo Bar.baz 'quux
%slurp_type1(Tks) ->
% case gsc_tokens:slurp_plist(Tks) of
% % head token is NOT open paren -> must be id/qid/tvar
% {slurp, [], [Tk | NewTks]} ->
% TkType = Tk#tk.type,
% case TkType of
% id -> {slurp, {token, Tk}, NewTks};
% qid -> {slurp, {token, Tk}, NewTks};
% tvar -> {slurp, {token, Tk}, NewTks};
% _ -> {error, {fixme, illegal_type, Tk, ?MODULE, ?LINE}}
% end;
% % head token is an open paren, need to recursively consume
% % the inner types
% {slurp, PListTokens, After} ->
% case gulp_ptype1(PListTokens) of
% {gulp, Types} -> {slurp, {plist, Types}, After};
% Error = {error, _} -> Error
% end;
% Error = {error, _} -> Error
% end.
%
%
%%slurp_type_expr_plist(Tks) ->
%% case gsc_tokens:slurp_plist(Tks) of
%% % head token is NOT open paren -> must be id/qid/tvar
%% {slurp, [], [Tk | NewTks]} ->
%% TkType = Tk#tk.type,
%% case TkType of
%% id -> {slurp, {token, Tk}, NewTks};
%% qid -> {slurp, {token, Tk}, NewTks};
%% tvar -> {slurp, {token, Tk}, NewTks};
%% _ -> {error, {fixme, illegal_type, Tk}}
%% end;
%% % head token is an open paren, need to recursively consume
%% % the inner types
%% {slurp, PListTokens, After} ->
%% case gulp_ptype1(PListTokens) of
%% {gulp, Types} -> {slurp, {plist, Types}, After};
%% Error = {error, _} -> Error
%% end;
%% Error = {error, _} -> Error
%% end.
%
%gulp_ptype1([#tk{string = "("}, #tk{string = ")"}]) ->
% {gulp, []};
%gulp_ptype1([#tk{string = "("} | Tail]) ->
% gulp_ptype1_II([], Tail).
%
%gulp_ptype1_II(Stk, Tks) ->
% case slurp_type_expr(Tks) of
% {slurp, NewType, [#tk{string = ")"}]} ->
% {gulp, lists:reverse([NewType | Stk])};
% {slurp, NewType, [#tk{string = ","} | NewTks]} ->
% gulp_ptype1_II([NewType | Stk], NewTks);
% Error = {error, _} ->
% Error
% end.
%
%
%%gulp_te_tk([Tk = #tk{type = TkType}])
%% when id =:= TkType;
%% qid =:= TkType;
%% tvar =:= TkType ->
%% {gulp, #ast_te_tk{token = Tk}};
%%gulp_te_tk(Tks) ->
%% {error, {fixme, nyi, gulp_te_tks, Tks}}.
%
%
%gulp_nyi(Tokens) ->
% {gulp, #ast_nyi{tokens = Tokens}}.
%
%
%drop(N, [_ | Xs]) when is_integer(N), N >= 1 ->
% drop(N-1, Xs);
%drop(_, []) ->
% [];
%drop(0, X) ->
% X.
%
%efmt(Fmt, Args) ->
% unicode:characters_to_list(io_lib:format(Fmt, Args)).
-216
View File
@@ -1,216 +0,0 @@
-module(gsc_parse_type_expr).
-export_type([
]).
-export([
unsafe_vtks_from_string/1,
gulp_vtks/1,
take_until_ifx_op/1
]).
-include("$gsc_include/gsc.hrl").
%------------------------------------------------------
% TYPES
%------------------------------------------------------
-type vtk_ifx_op() :: vtk_apply_to
| {'vtk_*', tk()}
| {'vtk_=>', tk()}.
-type vtk() :: tk()
| {vtk_plist, [tk()]}
| vtk_ifx_op().
-type gulped(X) :: {gulp, X}
| {error, any()}.
-type slurped(X) :: {slurp, X, Rest :: [tk()]}
| {error, any()}.
%------------------------------------------------------
% FUNCTIONS
%------------------------------------------------------
-spec unsafe_vtks_from_string(SrcStr) -> Vtks when
SrcStr :: string(),
Vtks :: [vtk()].
% @doc for testing
unsafe_vtks_from_string(S) ->
{ok, SigTks} = gsc_tokens:significant_tokens(S),
{gulp, Vtks} = gulp_vtks(SigTks),
Vtks.
% operators in descending order of exteriority
%
% precedence verbiage confuses me
%
% 1 + 2 * 3 ^ 4
%
% (+ 1 (* 2 (^ 3 4)))
%
% precedence is thinking about the operators as like
% having arms and legs and doing something. how much
% power do they have to bind to their neighbors. it's
% thinking of your operators as verbs in some state
% machine.
%
% i don't like when i have to model the state machine
% in my head when i'm reading code. everything should
% just be there. exteriority and interiority are
% properties of nouns, not properties of verbs.
%
% functional programming is all about offloading as
% much of your reasoning into nouns as possible. verbs
% change things. and change is bad.
-type chunk_strategy()
:: ast_parens % prefix paren (x y z)
| '=>' % infixr
| '*' % infix
| 'apply' % postfix paren f (...)
.
-record(ast_parens,
{open = none :: none | tk(),
inner = none :: none | [tk()],
close = none :: none | tk()}).
chunk_by(Strategy, Tokens) ->
chunk_by(Strategy, [], Tokens).
-spec chunk0(Strategy, Oks, Errs, Tokens) -> Result when
Strategy :: chunk_strategy(),
Oks :: [any()],
Errs :: [{error, Reason :: any()}],
Tokens :: [tk()],
Result :: {ok,
gulp_chunks_by(_, Stk, [], []) ->
{gulp, lists:reverse(Stk)};
gulp_chunks_by(_, _, Errs, []) ->
{error, {fixme, {?MODULE, ?LINE}, Errs}};
gulp_chunks_by(plist, Stk, Errs, Tokens) ->
case slurp_plist_rec(Tokens) of
{slurp, Plist, NewTokens} ->
gulp_chunks_by(plist, [PList | Stk], Errs, NewTokens);
barf ->
[Token | NewTokens] = Tokens,
gulp_chunks_by(plist, [Token | Stk], Errs, NewTokens);
Error ->
gulp_chunks_by(plist, Stk, [Error | Errs], Tokens);
end.
slurp_plist_rec(Tokens = [#tk{string = "(" | _]) ->
case gsc_tokens:slurp_plist(Tokens) of
{slurp, [], _} ->
barf;
{slurp, PTokens, NewTokens} ->
PTokensInner = pt_inner(PTokens),
end;
%-spec gulp_ifx_tree(Tokens) -> gulped(IfxTree) when
% Tokens :: [tk()],
% IfxTree :: ifx_tree().
%
%-spec chunk_by(ChunkStrategy, Tokens) -> Result when
% ChunkStrategy :: chunk_strategy(),
% Tokens :: [tk()],
% Result :: {ChunkStrategy,
-spec gulp_vtks(Tokens) -> Result when
Tokens :: [tk()],
Result :: gulped(VirtualTokens),
VirtualTokens :: [vtk()].
% @private
% for infix precedence/associativity resolution.
%
% better/original name (although misnomer) was
% insert_virtual_tokens. we're using the gulp verbiage
% because we're guarding against mistmatched delimiters
% @end
gulp_vtks(Tokens) ->
gulp_vtks([], Tokens).
gulp_vtks(Acc, Tks0) ->
% scan until next "(" | "*" | "=>"
case take_until_ifx_op(Tks0) of
% no infix op remaining, return arg
{_Pfx = Tks0,
_Sfx = []} ->
{gulp, lists:flatten([Acc, Tks0])};
% application
% "... foo(bar, baz) ..."
% ~> [..., foo, {plist, "(bar, baz)"}, ...]
{_Pfx = Tks1_BeforeOpen,
_Sfx = Tks2_OpenNAfter
= [#tk{string = "("} | _]} ->
case gsc_tokens:slurp_plist(Tks2_OpenNAfter) of
{slurp, Tks2A_OpenToClose, Tks2B_AfterClose} ->
NewAcc = [Acc,
Tks1_BeforeOpen,
vtk_apply_to,
{vtk_plist, Tks2A_OpenToClose}],
gulp_vtks(NewAcc, Tks2B_AfterClose);
Error = {error, _} ->
Error
end;
% product
{_Pfx = Tks0_BeforeTimes,
_Sfx = [ Tk1A_Times = #tk{string = "*"}
| Tks1B_AfterTimes]} ->
NewAcc = [Acc,
Tks0_BeforeTimes,
{'vtk_*', Tk1A_Times}],
gulp_vtks(NewAcc, Tks1B_AfterTimes);
% funType
{_Pfx = Tks0_BeforeOp,
_Sfx = [ Tk1A_Op = #tk{string = "=>"}
| Tks1B_AfterOp]} ->
NewAcc = [Acc,
Tks0_BeforeOp,
{'vtk_=>', Tk1A_Op}],
gulp_vtks(NewAcc, Tks1B_AfterOp)
end.
-spec take_until_ifx_op(Tokens) -> Result when
Tokens :: [tk()],
Result :: {Taken, NewTokens},
Taken :: Tokens,
NewTokens :: Tokens.
% @doc
% consume tokens until one of ["(", "*", "=>"]
take_until_ifx_op(Tks) ->
take_until_ifx_op([], Tks).
take_until_ifx_op(Stack, []) ->
{lists:reverse(Stack), []};
take_until_ifx_op(Stack, Tokens = [Token | NewTokens]) ->
TokStr = Token#tk.string,
Continue =
case TokStr of
% exit cases
"(" -> false;
"*" -> false;
"=>" -> false;
_ -> true
end,
case Continue of
true -> take_until_ifx_op([Token | Stack], NewTokens);
false -> {lists:reverse(Stack), Tokens}
end.
+16 -16
View File
@@ -1,6 +1,6 @@
% @doc compatibility layer to test against so_scan
%
% converts gsc_tokens data to so_scan tokens
% converts gs_tokens data to so_scan tokens
%
% Ref: so_scan.erl
-module(gso_scan).
@@ -76,14 +76,14 @@
-type so_symbol() :: so_kwd() | so_special_char() | atom().
-type so_token2() :: {Symbol :: so_symbol(),
Location :: gsc_pos()}.
Location :: tk_pos()}.
% FIXME
% this is 'id', 'con', qid
-type so_tk3type() :: char | string | hex | int | bytes | qid | qcon | tvar | id | con.
-type so_token3() :: {TokenType :: so_tk3type(),
Location :: gsc_pos(),
Location :: tk_pos(),
TokenValue :: term()}.
-type so_token() :: so_token2() | so_token3().
@@ -104,7 +104,7 @@
% @end
scan(SrcStr) ->
case gsc_tokens:tokens(SrcStr) of
case gs_tokens:tokens(SrcStr) of
{ok, SfLTokens} ->
SoTokens = to_so_tokens(SfLTokens),
{ok, SoTokens};
@@ -151,12 +151,12 @@ scan(SrcStr) ->
%
% so if we see an ak/ct/sg token, we summon evil ben
% carson to reconjoin the unconjoined twins
to_so_tokens([ AkTok = #tk{type = AkCtSg, pos = Pos}
to_so_tokens([ AkTok = #tk{shape = AkCtSg, pos = Pos}
| Sheeit])
when ak =:= AkCtSg;
ct =:= AkCtSg;
sg =:= AkCtSg ->
{#tk{string = FinalAkStr}, NewSheeit}
{#tk{str = FinalAkStr}, NewSheeit}
= ken_barson_rises(AkTok, Sheeit),
[{id, Pos, FinalAkStr}| to_so_tokens(NewSheeit)];
% this part is just lists:filtermap
@@ -259,9 +259,9 @@ to_so_tokens([]) ->
% `_`**: `smr_plus` requires >=1 base58 char to
% match; `ak_I`, `ak_0`, `ak__bar` all fall
% through to `id` and both tokenizers agree.
ken_barson_rises(AkTokAcc = #tk{string = AkStr},
SrcTokens = [#tk{type = CandidateType,
string = CandidateString}
ken_barson_rises(AkTokAcc = #tk{str = AkStr},
SrcTokens = [#tk{shape = CandidateType,
str = CandidateString}
| Rest]) ->
% candidate:
% dig out the token type and the string
@@ -273,7 +273,7 @@ ken_barson_rises(AkTokAcc = #tk{string = AkStr},
Smash ->
% dig out the token from LcTokApi
NewAkStr = AkStr ++ CandidateString,
NewAkTokAcc = AkTokAcc#tk{string = NewAkStr},
NewAkTokAcc = AkTokAcc#tk{str = NewAkStr},
ken_barson_rises(NewAkTokAcc, Rest);
Pass ->
{AkTokAcc, SrcTokens}
@@ -320,9 +320,9 @@ pass_types() ->
% follow-on tokens
% @end
to_so_token(#tk{type = SfTokenType,
pos = Pos,
string = SfTokenStr}) ->
to_so_token(#tk{shape = SfTokenType,
pos = Pos,
str = SfTokenStr}) ->
case SfTokenType of
%-----------------
% Ignored
@@ -371,7 +371,7 @@ to_so_token(#tk{type = SfTokenType,
NYI ->
Msg = io_lib:format("gsc_so_scan:to_so_token/1: unhandled token shape: ~p", [NYI]),
error(#gsc_err{atom = nyi,
string = Msg})
str = Msg})
end.
%% ak/ct/sg all tokenize to id
@@ -393,7 +393,7 @@ so_parse_char([$' | Chars]) ->
[Char] -> Char;
_Bad ->
error(#gsc_err{atom = bad_token,
string = "Bad character literal: '" ++ Chars})
str = "Bad character literal: '" ++ Chars})
end.
so_parse_string([$" | Chars]) ->
@@ -435,7 +435,7 @@ unescape(Delim, [$\\, Code | Chars], Acc) ->
$t -> Ok($\t);
$v -> Ok($\v);
_ -> error(#gsc_err{atom = bad_escape_char,
string = "Bad control sequence: \\" ++ [Code]}) %% TODO
str = "Bad control sequence: \\" ++ [Code]}) %% TODO
end;
unescape(Delim, [C | Chars], Acc) ->
unescape(Delim, Chars, [C | Acc]).