Skip to content

Commit

Permalink
Make parser suggestions an opt-in feature that can be compiled into p…
Browse files Browse the repository at this point in the history
…lutoc rather than something that's always there
  • Loading branch information
Sainan authored and well-in-that-case committed Feb 17, 2024
1 parent ce9d65c commit c2d26d2
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/llex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ static const char *const luaX_tokens [] = {
"pluto_switch", "pluto_continue", "pluto_enum", "pluto_new", "pluto_class", "pluto_parent", "pluto_export", "pluto_try", "pluto_catch",
"switch", "continue", "enum", "new", "class", "parent", "export", "try", "catch",
"let", "const", "global",
#ifdef PLUTO_PARSER_SUGGESTIONS
"pluto_suggest_0", "pluto_suggest_1",
#endif
"return", "then", "true", "until", "while",
"//", "..", "...", "==", ">=", "<=", "~=", "<=>",
"<<", ">>", "::", "<eof>",
Expand Down
15 changes: 13 additions & 2 deletions src/llex.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ enum RESERVED {
TK_PSWITCH, TK_PCONTINUE, TK_PENUM, TK_PNEW, TK_PCLASS, TK_PPARENT, TK_PEXPORT, TK_PTRY, TK_PCATCH,
TK_SWITCH, TK_CONTINUE, TK_ENUM, TK_NEW, TK_CLASS, TK_PARENT, TK_EXPORT, TK_TRY, TK_CATCH, // New non-compatible keywords.
TK_LET, TK_CONST, TK_GLOBAL, // New optional keywords.
#ifdef PLUTO_PARSER_SUGGESTIONS
TK_SUGGEST_0, TK_SUGGEST_1, // New special keywords.
#endif
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
/* other terminal symbols */
TK_IDIV, TK_CONCAT, TK_DOTS,
Expand All @@ -69,7 +71,12 @@ enum RESERVED {

#define END_COMPAT FIRST_NON_COMPAT
#define END_NON_COMPAT FIRST_OPTIONAL
#ifdef PLUTO_PARSER_SUGGESTIONS
#define END_OPTIONAL FIRST_SPECIAL
#define END_SPECIAL TK_RETURN
#else
#define END_OPTIONAL TK_RETURN
#endif

/* number of reserved words */
#define NUM_RESERVED (cast_int(LAST_RESERVED-FIRST_RESERVED + 1))
Expand Down Expand Up @@ -143,12 +150,14 @@ struct Token {
}

[[nodiscard]] bool IsOptional() const noexcept {
return (token >= FIRST_OPTIONAL && token < FIRST_SPECIAL);
return (token >= FIRST_OPTIONAL && token < END_OPTIONAL);
}

#ifdef PLUTO_PARSER_SUGGESTIONS
[[nodiscard]] bool IsSpecial() const noexcept {
return (token >= FIRST_SPECIAL && token < TK_RETURN);
return (token >= FIRST_SPECIAL && token < END_SPECIAL);
}
#endif

[[nodiscard]] bool IsOverridable() const noexcept {
return token == TK_PARENT || token == TK_PPARENT;
Expand Down Expand Up @@ -513,9 +522,11 @@ struct LexState {
;
}

#ifdef PLUTO_PARSER_SUGGESTIONS
[[nodiscard]] bool shouldSuggest() const noexcept {
return t.token == TK_SUGGEST_0 || t.token == TK_SUGGEST_1;
}
#endif

[[nodiscard]] bool isKeywordEnabled(int t) const noexcept {
static_assert((KS_ENABLED_BY_USER & 1) == 0);
Expand Down
30 changes: 26 additions & 4 deletions src/lparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -279,10 +279,12 @@ static bool testnext2 (LexState *ls, int token1, int token2) {
** Check that next token is 'c'.
*/
static void check (LexState *ls, int c) {
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", luaX_token2str_noq(ls, c));
}
#endif
if (ls->t.token != c) {
error_expected(ls, c);
}
Expand Down Expand Up @@ -365,10 +367,12 @@ enum NameFlags {

static TString *str_checkname (LexState *ls, int flags = N_RESERVED_NON_VALUE) {
TString *ts;
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.pushLocals();
}
#endif
if (!isnametkn(ls, flags)) {
if (ls->t.IsNonCompatible()) {
throwerr(ls, luaO_fmt(ls->L, "expected a name, found %s", luaX_token2str(ls, ls->t.token)), luaO_fmt(ls->L, "%s has a different meaning in Pluto, but you can disable this: https://pluto.do/compat", luaX_token2str(ls, ls->t.token)));
Expand Down Expand Up @@ -1564,11 +1568,13 @@ static void funcfield (LexState *ls, struct ConsControl *cc, int ismethod) {

static void field (LexState *ls, ConsControl *cc, bool for_class = false) {
/* field -> listfield | recfield | funcfield */
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", "function");
ss.push("stat", "static");
}
#endif
if (ls->t.IsReserved() && luaX_lookahead(ls) == '=') {
prenamedfield(ls, cc, luaX_reserved2str(ls->t.token));
}
Expand Down Expand Up @@ -1689,13 +1695,15 @@ static void classname (LexState *ls, expdesc *v) {

static size_t checkextends (LexState *ls) {
size_t pos = 0;
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", "extends");
ss.push("stat", "function");
ss.push("stat", "static");
ss.push("stat", "end");
}
#endif
if (ls->t.token == TK_EXTENDS) {
luaX_next(ls);
pos = luaX_getpos(ls);
Expand Down Expand Up @@ -2624,13 +2632,15 @@ static void enumexp (LexState *ls, expdesc *v, TString *varname) {
switch (ls->t.token) {
case ':': {
luaX_next(ls);
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("efunc", "values");
ss.push("efunc", "names");
ss.push("efunc", "kvmap");
ss.push("efunc", "vkmap");
}
#endif
check(ls, TK_NAME);
if (strcmp(ls->t.seminfo.ts->contents, "values") == 0) {
luaX_next(ls);
Expand Down Expand Up @@ -2698,12 +2708,14 @@ static void enumexp (LexState *ls, expdesc *v, TString *varname) {
case '.': {
const EnumDesc* ed = &ls->enums.at((size_t)v->u.ival);
luaX_next(ls);
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
for (const auto& e : ed->enumerators) {
ss.push("eprop", e.name->contents, std::to_string(e.value));
}
}
#endif
check(ls, TK_NAME);
for (const auto& e : ed->enumerators) {
if (eqstr(e.name, ls->t.seminfo.ts)) {
Expand Down Expand Up @@ -3606,10 +3618,12 @@ static BinOpr subexpr (LexState *ls, expdesc *v, int limit, TypeHint *prop, int


static void expr (LexState *ls, expdesc *v, TypeHint *prop, int flags) {
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.pushLocals();
}
#endif
subexpr(ls, v, 0, prop, flags);
if (testnext(ls, '?')) { /* ternary expression? */
if (prop) prop->clear(); /* we don't care what type the condition is/was */
Expand Down Expand Up @@ -4589,12 +4603,14 @@ static void retstat (LexState *ls, TypeHint *prop) {


static int checkkeyword (LexState *ls) {
if (ls->t.token == TK_NAME)
for (int i = FIRST_NON_COMPAT; i != FIRST_SPECIAL; ++i)
if (ls->t.token == TK_NAME) {
for (int i = FIRST_NON_COMPAT; i != END_OPTIONAL; ++i) {
if (strcmp(luaX_reserved2str(i), ls->t.seminfo.ts->contents) == 0) {
luaX_next(ls);
return i;
}
}
}
if (!ls->t.IsNonCompatible() && !ls->t.IsOptional()) {
if (ls->t.IsCompatible())
luaX_syntaxerror(ls, "expected non-compatible keyword");
Expand Down Expand Up @@ -4655,7 +4671,7 @@ static void usestat (LexState *ls) {
std::vector<int> tokens{};
if (ls->t.token == '*') {
is_all = true;
for (int i = FIRST_NON_COMPAT; i != FIRST_SPECIAL; ++i) {
for (int i = FIRST_NON_COMPAT; i != END_OPTIONAL; ++i) {
tokens.emplace_back(i);
}
luaX_next(ls);
Expand Down Expand Up @@ -4711,7 +4727,7 @@ static void usestat (LexState *ls) {
if (is_all || is_version) {
if (is_version) {
/* disable all non-compatible keywords as of this Pluto version, then enable those from the elected Pluto version. */
for (int i = FIRST_NON_COMPAT; i != FIRST_SPECIAL; ++i) {
for (int i = FIRST_NON_COMPAT; i != END_OPTIONAL; ++i) {
if (ls->isKeywordEnabled(i)) {
ls->setKeywordState(i, KS_DISABLED_BY_USER);
disablekeyword(ls, i, false);
Expand Down Expand Up @@ -4931,6 +4947,7 @@ static void globalstat (LexState *ls) {


static void statement (LexState *ls, TypeHint *prop) {
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", "if");
Expand Down Expand Up @@ -4960,6 +4977,7 @@ static void statement (LexState *ls, TypeHint *prop) {
ss.pushLocals();
return;
}
#endif
int line = ls->getLineNumber();
if (ls->t.token != ';') {
if (ls->laststat.IsEscapingToken()
Expand Down Expand Up @@ -5023,13 +5041,15 @@ static void statement (LexState *ls, TypeHint *prop) {
[[fallthrough]];
case TK_LOCAL: { /* stat -> localstat */
luaX_next(ls); /* skip LOCAL */
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", "function");
ss.push("stat", "class");
ss.push("stat", "pluto_class");
return;
}
#endif
if (testnext(ls, TK_FUNCTION)) /* local function? */
localfunc(ls);
else if (testnext2(ls, TK_CLASS, TK_PCLASS))
Expand All @@ -5047,13 +5067,15 @@ static void statement (LexState *ls, TypeHint *prop) {
case TK_EXPORT:
case TK_PEXPORT: {
luaX_next(ls); /* skip EXPORT */
#ifdef PLUTO_PARSER_SUGGESTIONS
if (ls->shouldSuggest()) {
SuggestionsState ss(ls);
ss.push("stat", "function");
ss.push("stat", "class");
ss.push("stat", "pluto_class");
break;
}
#endif
if (testnext(ls, TK_FUNCTION)) {
ls->fs->bl->export_symbols.emplace_back(str_checkname(ls, 0));
luaX_prev(ls);
Expand Down
2 changes: 2 additions & 0 deletions src/lsuggestions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include "llex.h"

#ifdef PLUTO_PARSER_SUGGESTIONS
struct SuggestionsState {
struct Suggestion {
const char* type;
Expand Down Expand Up @@ -76,3 +77,4 @@ struct SuggestionsState {
}
}
};
#endif

0 comments on commit c2d26d2

Please sign in to comment.