diff --git a/fish.xcodeproj/project.pbxproj b/fish.xcodeproj/project.pbxproj index 9c365ccbc..1067cbeb8 100644 --- a/fish.xcodeproj/project.pbxproj +++ b/fish.xcodeproj/project.pbxproj @@ -703,6 +703,7 @@ D025C02815D1FEA100B9DB63 /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = ""; }; D025C02915D1FEA100B9DB63 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = ""; }; D02960E51FBD726100CA3985 /* builtin_wait.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_wait.cpp; sourceTree = ""; }; + D0301C1D2002B90500B1F463 /* parse_grammar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_grammar.h; sourceTree = ""; }; D031890915E36D9800D9CC39 /* base */ = {isa = PBXFileReference; lastKnownFileType = text; path = base; sourceTree = BUILT_PRODUCTS_DIR; }; D03238891849D1980032CF2C /* pager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = pager.cpp; sourceTree = ""; }; D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = ""; }; @@ -1232,6 +1233,7 @@ D03238891849D1980032CF2C /* pager.cpp */, D0A0851B13B3ACEE0099B651 /* parse_util.h */, D0A0855213B3ACEE0099B651 /* parse_util.cpp */, + D0301C1D2002B90500B1F463 /* parse_grammar.h */, D0A0851C13B3ACEE0099B651 /* parser_keywords.h */, D0A0855313B3ACEE0099B651 /* parser_keywords.cpp */, D0A0851D13B3ACEE0099B651 /* parser.h */, diff --git a/src/parse_grammar.h b/src/parse_grammar.h new file mode 100644 index 000000000..23e79bd79 --- /dev/null +++ b/src/parse_grammar.h @@ -0,0 +1,163 @@ +// Programmatic representation of fish grammar +#ifndef FISH_PARSE_GRAMMAR_H +#define FISH_PARSE_GRAMMAR_H + +#include "parse_constants.h" +#include "tokenizer.h" + +namespace grammar { +using production_element_t = uint8_t; + +// Forward declarations. +#define ELEM(T) struct T; +#include "parse_grammar_elements.inc" + +// A production is a sequence of production elements. +template +struct production_t { + production_element_t elems[count]; +}; + +template +struct prim {}; + +using tok_end = prim; +using tok_string = prim; + +template +struct keyword {}; + +// A producer holds various productions. +template +struct producer {}; + +// Empty produces nothing. +struct empty : public producer> {}; + +// Not sure if we need this. +template +struct single {}; + +template +using produces_single = producer>; + +// Alternative represents a choice. +template +struct alternative {}; + +template +using produces_alternative = producer>; + +// Sequence represents a list of productions. +template +struct seq {}; + +template +using produces_sequence = producer>; + +// Following are the grammar productions. +#define DEF(T) struct T : public + +// A job_list is a list of jobs, separated by semicolons or newlines +DEF(job_list) +produces_alternative, // + seq>{}; + +// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases +// like if statements, where we require a command). To represent "non-empty", we require a +// statement, followed by a possibly empty job_continuation, and then optionally a background +// specifier '&' +DEF(job) produces_sequence{}; + +DEF(job_continuation) +produces_alternative, statement, job_continuation>>{}; + +// A statement is a normal command, or an if / while / and etc +DEF(statement) +produces_alternative{}; + +// A block is a conditional, loop, or begin/end +DEF(if_statement) +produces_sequence{}; + +DEF(if_clause) +produces_sequence, job, tok_end, andor_job_list, job_list>{}; + +DEF(else_clause) produces_alternative, else_continuation>>{}; +DEF(else_continuation) +produces_alternative, // + seq>{}; + +DEF(switch_statement) +produces_sequence, argument, tok_end, case_item_list, end_command, + arguments_or_redirections_list>{}; + +DEF(case_item_list) +produces_alternative, // + seq>{}; + +DEF(case_item) produces_sequence, argument_list, tok_end, job_list>{}; + +DEF(block_statement) +produces_sequence{}; +DEF(block_header) produces_alternative{}; + +DEF(for_header) +produces_sequence, tok_string, keyword, argument_list, + tok_end>{}; + +DEF(while_header) produces_sequence, job, tok_end, andor_job_list>{}; + +struct begin_header : produces_single> {}; + +// Functions take arguments, and require at least one (the name). No redirections allowed. +DEF(function_header) +produces_sequence, argument, argument_list, tok_end>{}; + +// A boolean statement is AND or OR or NOT +DEF(boolean_statement) +produces_alternative, statement>, // + seq, statement>, // + seq, statement>>{}; +// An andor_job_list is zero or more job lists, where each starts with an `and` or `or` boolean +// statement. +DEF(andor_job_list) +produces_alternative, // + seq>{}; + +// A decorated_statement is a command with a list of arguments_or_redirections, possibly with +// "builtin" or "command" or "exec" +DEF(decorated_statement) +produces_alternative, plain_statement>, // + seq, plain_statement>, // + seq, plain_statement>>{}; + +DEF(plain_statement) produces_sequence{}; + +DEF(argument_list) produces_alternative>{}; +DEF(arguments_or_redirections_list) +produces_alternative>{}; + +DEF(argument_or_redirection) produces_alternative{}; +DEF(argument) produces_single{}; +DEF(optional_background) produces_alternative>{}; +DEF(end_command) produces_single>{}; + +// A freestanding_argument_list is equivalent to a normal argument list, except it may contain +// TOK_END (newlines, and even semicolons, for historical reasons) +DEF(freestanding_argument_list) +produces_alternative, // + seq>{}; +} +#endif diff --git a/src/parse_grammar_elements.inc b/src/parse_grammar_elements.inc new file mode 100644 index 000000000..6f38cf99d --- /dev/null +++ b/src/parse_grammar_elements.inc @@ -0,0 +1,32 @@ +// Define ELEM before including this file. +ELEM(job_list) +ELEM(job) +ELEM(job_continuation) +ELEM(statement) +ELEM(if_statement) +ELEM(if_clause) +ELEM(else_clause) +ELEM(else_continuation) +ELEM(switch_statement) +ELEM(case_item_list) +ELEM(case_item) +ELEM(block_statement) +ELEM(block_header) +ELEM(for_header) +ELEM(while_header) +ELEM(begin_header) +ELEM(function_header) +ELEM(boolean_statement) +ELEM(andor_job_list) +ELEM(decorated_statement) +ELEM(plain_statement) +ELEM(argument_list) +ELEM(arguments_or_redirections_list) +ELEM(argument_or_redirection) +ELEM(argument) +ELEM(redirection) +ELEM(optional_background) +ELEM(end_command) +ELEM(freestanding_argument_list) +#undef ELEM + diff --git a/src/parse_productions.cpp b/src/parse_productions.cpp index 7a66f9d7d..ae0df3303 100644 --- a/src/parse_productions.cpp +++ b/src/parse_productions.cpp @@ -4,6 +4,7 @@ #include "common.h" #include "parse_constants.h" +#include "parse_grammar.h" #include "parse_productions.h" #include "parse_tree.h"