mirror of
https://github.com/fish-shell/fish-shell
synced 2024-12-26 12:53:13 +00:00
Add grammar in type system
This commit is contained in:
parent
0a4883a6b8
commit
a012320a9a
4 changed files with 198 additions and 0 deletions
|
@ -703,6 +703,7 @@
|
|||
D025C02815D1FEA100B9DB63 /* functions */ = {isa = PBXFileReference; lastKnownFileType = folder; name = functions; path = share/functions; sourceTree = "<group>"; };
|
||||
D025C02915D1FEA100B9DB63 /* tools */ = {isa = PBXFileReference; lastKnownFileType = folder; name = tools; path = share/tools; sourceTree = "<group>"; };
|
||||
D02960E51FBD726100CA3985 /* builtin_wait.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = builtin_wait.cpp; sourceTree = "<group>"; };
|
||||
D0301C1D2002B90500B1F463 /* parse_grammar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = parse_grammar.h; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
D032388A1849D1980032CF2C /* pager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = pager.h; sourceTree = "<group>"; };
|
||||
|
@ -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 */,
|
||||
|
|
163
src/parse_grammar.h
Normal file
163
src/parse_grammar.h
Normal file
|
@ -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 <int WHICH, uint8_t count = 0>
|
||||
struct production_t {
|
||||
production_element_t elems[count];
|
||||
};
|
||||
|
||||
template <int TOKEN>
|
||||
struct prim {};
|
||||
|
||||
using tok_end = prim<TOK_END>;
|
||||
using tok_string = prim<TOK_STRING>;
|
||||
|
||||
template <int WHICH>
|
||||
struct keyword {};
|
||||
|
||||
// A producer holds various productions.
|
||||
template <typename T>
|
||||
struct producer {};
|
||||
|
||||
// Empty produces nothing.
|
||||
struct empty : public producer<production_t<0>> {};
|
||||
|
||||
// Not sure if we need this.
|
||||
template <class A>
|
||||
struct single {};
|
||||
|
||||
template <class A>
|
||||
using produces_single = producer<single<A>>;
|
||||
|
||||
// Alternative represents a choice.
|
||||
template <class A1, class A2, class A3 = empty, class A4 = empty, class A5 = empty>
|
||||
struct alternative {};
|
||||
|
||||
template <class... Args>
|
||||
using produces_alternative = producer<alternative<Args...>>;
|
||||
|
||||
// Sequence represents a list of productions.
|
||||
template <class A1, class A2, class A3 = empty, class A4 = empty, class A5 = empty,
|
||||
class A6 = empty>
|
||||
struct seq {};
|
||||
|
||||
template <class... Args>
|
||||
using produces_sequence = producer<seq<Args...>>;
|
||||
|
||||
// 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<empty, //
|
||||
seq<job, job_list>, //
|
||||
seq<job, job_list>>{};
|
||||
|
||||
// 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<statement, job_continuation, optional_background>{};
|
||||
|
||||
DEF(job_continuation)
|
||||
produces_alternative<empty, //
|
||||
seq<prim<TOK_PIPE>, statement, job_continuation>>{};
|
||||
|
||||
// A statement is a normal command, or an if / while / and etc
|
||||
DEF(statement)
|
||||
produces_alternative<boolean_statement, //
|
||||
block_statement, //
|
||||
if_statement, //
|
||||
switch_statement, //
|
||||
decorated_statement>{};
|
||||
|
||||
// A block is a conditional, loop, or begin/end
|
||||
DEF(if_statement)
|
||||
produces_sequence<if_clause, else_clause, end_command, arguments_or_redirections_list>{};
|
||||
|
||||
DEF(if_clause)
|
||||
produces_sequence<keyword<parse_keyword_if>, job, tok_end, andor_job_list, job_list>{};
|
||||
|
||||
DEF(else_clause) produces_alternative<empty, seq<keyword<parse_keyword_else>, else_continuation>>{};
|
||||
DEF(else_continuation)
|
||||
produces_alternative<seq<if_clause, else_clause>, //
|
||||
seq<tok_end, job_list>>{};
|
||||
|
||||
DEF(switch_statement)
|
||||
produces_sequence<keyword<parse_keyword_switch>, argument, tok_end, case_item_list, end_command,
|
||||
arguments_or_redirections_list>{};
|
||||
|
||||
DEF(case_item_list)
|
||||
produces_alternative<empty, //
|
||||
seq<case_item, case_item_list>, //
|
||||
seq<tok_end, case_item_list>>{};
|
||||
|
||||
DEF(case_item) produces_sequence<keyword<parse_keyword_case>, argument_list, tok_end, job_list>{};
|
||||
|
||||
DEF(block_statement)
|
||||
produces_sequence<block_header, job_list, end_command, arguments_or_redirections_list>{};
|
||||
DEF(block_header) produces_alternative<for_header, while_header, function_header, begin_header>{};
|
||||
|
||||
DEF(for_header)
|
||||
produces_sequence<keyword<parse_keyword_for>, tok_string, keyword<parse_keyword_in>, argument_list,
|
||||
tok_end>{};
|
||||
|
||||
DEF(while_header) produces_sequence<keyword<parse_keyword_while>, job, tok_end, andor_job_list>{};
|
||||
|
||||
struct begin_header : produces_single<keyword<parse_keyword_begin>> {};
|
||||
|
||||
// Functions take arguments, and require at least one (the name). No redirections allowed.
|
||||
DEF(function_header)
|
||||
produces_sequence<keyword<parse_keyword_function>, argument, argument_list, tok_end>{};
|
||||
|
||||
// A boolean statement is AND or OR or NOT
|
||||
DEF(boolean_statement)
|
||||
produces_alternative<seq<keyword<parse_keyword_and>, statement>, //
|
||||
seq<keyword<parse_keyword_or>, statement>, //
|
||||
seq<keyword<parse_keyword_not>, 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<empty, //
|
||||
seq<job, andor_job_list>, //
|
||||
seq<tok_end, andor_job_list>>{};
|
||||
|
||||
// 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<keyword<parse_keyword_command>, plain_statement>, //
|
||||
seq<keyword<parse_keyword_builtin>, plain_statement>, //
|
||||
seq<keyword<parse_keyword_exec>, plain_statement>>{};
|
||||
|
||||
DEF(plain_statement) produces_sequence<tok_string, arguments_or_redirections_list>{};
|
||||
|
||||
DEF(argument_list) produces_alternative<empty, seq<argument, argument_list>>{};
|
||||
DEF(arguments_or_redirections_list)
|
||||
produces_alternative<empty, seq<argument_or_redirection, arguments_or_redirections_list>>{};
|
||||
|
||||
DEF(argument_or_redirection) produces_alternative<argument, redirection>{};
|
||||
DEF(argument) produces_single<tok_string>{};
|
||||
DEF(optional_background) produces_alternative<empty, prim<TOK_BACKGROUND>>{};
|
||||
DEF(end_command) produces_single<keyword<parse_keyword_end>>{};
|
||||
|
||||
// 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<empty, //
|
||||
seq<argument, freestanding_argument_list>, //
|
||||
seq<tok_end, freestanding_argument_list>>{};
|
||||
}
|
||||
#endif
|
32
src/parse_grammar_elements.inc
Normal file
32
src/parse_grammar_elements.inc
Normal file
|
@ -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
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "common.h"
|
||||
#include "parse_constants.h"
|
||||
#include "parse_grammar.h"
|
||||
#include "parse_productions.h"
|
||||
#include "parse_tree.h"
|
||||
|
||||
|
|
Loading…
Reference in a new issue