2016-05-01 11:31:02 +08:00
|
|
|
// Prototypes for functions for storing and retrieving function information. These functions also
|
|
|
|
// take care of autoloading functions in the $fish_function_path. Actual function evaluation is
|
|
|
|
// taken care of by the parser and to some degree the builtin handling library.
|
2005-10-04 23:11:39 +08:00
|
|
|
#ifndef FISH_FUNCTION_H
|
|
|
|
#define FISH_FUNCTION_H
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
#include <map>
|
2022-08-21 14:14:48 +08:00
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
2005-10-04 23:11:39 +08:00
|
|
|
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
#include "ast.h"
|
2012-01-14 15:44:18 +08:00
|
|
|
#include "common.h"
|
2018-02-10 13:53:06 +08:00
|
|
|
#include "parse_tree.h"
|
2005-10-04 23:11:39 +08:00
|
|
|
|
2012-01-17 04:10:08 +08:00
|
|
|
class parser_t;
|
|
|
|
|
2018-02-12 08:37:38 +08:00
|
|
|
/// A function's constant properties. These do not change once initialized.
|
|
|
|
struct function_properties_t {
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
function_properties_t();
|
|
|
|
function_properties_t(const function_properties_t &other);
|
|
|
|
function_properties_t &operator=(const function_properties_t &other);
|
|
|
|
|
2018-02-12 08:37:38 +08:00
|
|
|
/// Parsed source containing the function.
|
Port AST to Rust
The translation is fairly direct though it adds some duplication, for example
there are multiple "match" statements that mimic function overloading.
Rust has no overloading, and we cannot have generic methods in the Node trait
(due to a Rust limitation, the error is like "cannot be made into an object")
so we include the type name in method names.
Give clients like "indent_visitor_t" a Rust companion ("IndentVisitor")
that takes care of the AST traversal while the AST consumption remains
in C++ for now. In future, "IndentVisitor" should absorb the entirety of
"indent_visitor_t". This pattern requires that "fish_indent" be exposed
includable header to the CXX bridge.
Alternatively, we could define FFI wrappers for recursive AST traversal.
Rust requires we separate the AST visitors for "mut" and "const"
scenarios. Take this opportunity to concretize both visitors:
The only client that requires mutable access is the populator. To match the
structure of the C++ populator which makes heavy use of function overloading,
we need to add a bunch of functions to the trait. Since there is no other
mutable visit, this seems acceptable.
The "const" visitors never use "will_visit_fields_of()" or
"did_visit_fields_of()", so remove them (though this is debatable).
Like in the C++ implementation, the AST nodes themselves are largely defined
via macros. Union fields like "Statement" and "ArgumentOrRedirection"
do currently not use macros but may in future.
This commit also introduces a precedent for a type that is defined in one
CXX bridge and used in another one - "ParseErrorList". To make this work
we need to manually define "ExternType".
There is one annoyance with CXX: functions that take explicit lifetime
parameters require to be marked as unsafe. This makes little sense
because functions that return `&Foo` with implicit lifetime can be
misused the same way on the C++ side.
One notable change is that we cannot directly port "find_block_open_keyword()"
(which is used to compute an error) because it relies on the stack of visited
nodes. We cannot modify a stack of node references while we do the "mut"
walk. Happily, an idiomatic solution is easy: we can tell the AST visitor
to backtrack to the parent node and create the error there.
Since "node_t::accept_base" is no longer a template we don't need the
"node_visitation_t" trampoline anymore.
The added copying at the FFI boundary makes things slower (memcpy dominates
the profile) but it's not unusable, which is good news:
$ hyperfine ./fish.{old,new}" -c 'source ../share/completions/git.fish'"
Benchmark 1: ./fish.old -c 'source ../share/completions/git.fish'
Time (mean ± σ): 195.5 ms ± 2.9 ms [User: 190.1 ms, System: 4.4 ms]
Range (min … max): 193.2 ms … 205.1 ms 15 runs
Benchmark 2: ./fish.new -c 'source ../share/completions/git.fish'
Time (mean ± σ): 677.5 ms ± 62.0 ms [User: 665.4 ms, System: 10.0 ms]
Range (min … max): 611.7 ms … 805.5 ms 10 runs
Summary
'./fish.old -c 'source ../share/completions/git.fish'' ran
3.47 ± 0.32 times faster than './fish.new -c 'source ../share/completions/git.fish''
Leftovers:
- Enum variants are still snakecase; I didn't get around to changing this yet.
- "ast_type_to_string()" still returns a snakecase name. This could be
changed since it's not user visible.
2023-04-02 22:42:59 +08:00
|
|
|
rust::Box<parsed_source_ref_t> parsed_source;
|
2018-02-12 08:37:38 +08:00
|
|
|
|
2020-01-04 06:40:28 +08:00
|
|
|
/// Node containing the function statement, pointing into parsed_source.
|
|
|
|
/// We store block_statement, not job_list, so that comments attached to the header are
|
|
|
|
/// preserved.
|
2020-07-04 02:16:51 +08:00
|
|
|
const ast::block_statement_t *func_node;
|
2018-02-12 08:37:38 +08:00
|
|
|
|
|
|
|
/// List of all named arguments for this function.
|
2023-04-19 06:19:10 +08:00
|
|
|
std::vector<wcstring> named_arguments;
|
2018-02-12 08:37:38 +08:00
|
|
|
|
2021-10-22 04:56:32 +08:00
|
|
|
/// Description of the function.
|
|
|
|
wcstring description;
|
|
|
|
|
2019-11-13 01:53:10 +08:00
|
|
|
/// Mapping of all variables that were inherited from the function definition scope to their
|
|
|
|
/// values.
|
2023-04-19 06:19:10 +08:00
|
|
|
std::map<wcstring, std::vector<wcstring>> inherit_vars;
|
2019-11-13 01:53:10 +08:00
|
|
|
|
2018-02-12 08:37:38 +08:00
|
|
|
/// Set to true if invoking this function shadows the variables of the underlying function.
|
2019-05-05 07:47:27 +08:00
|
|
|
bool shadow_scope{true};
|
2021-10-22 04:10:09 +08:00
|
|
|
|
2021-10-22 04:23:49 +08:00
|
|
|
/// Whether the function was autoloaded.
|
|
|
|
bool is_autoload{false};
|
|
|
|
|
2022-08-14 03:15:23 +08:00
|
|
|
/// The file from which the function was created, or nullptr if not from a file.
|
|
|
|
filename_ref_t definition_file{};
|
2021-10-22 05:28:39 +08:00
|
|
|
|
2023-02-13 23:59:28 +08:00
|
|
|
/// Whether the function was copied.
|
|
|
|
bool is_copy{false};
|
|
|
|
|
|
|
|
/// The file from which the function was copied, or nullptr if not from a file.
|
|
|
|
filename_ref_t copy_definition_file{};
|
|
|
|
|
|
|
|
/// The line number where the specified function was copied.
|
|
|
|
int copy_definition_lineno{};
|
|
|
|
|
2021-10-22 05:28:39 +08:00
|
|
|
/// \return the description, localized via _.
|
|
|
|
const wchar_t *localized_description() const;
|
|
|
|
|
|
|
|
/// \return the line number where the definition of the specified function started.
|
|
|
|
int definition_lineno() const;
|
|
|
|
|
|
|
|
/// \return a definition of the function, annotated with properties like event handlers and wrap
|
|
|
|
/// targets. This is to support the 'functions' builtin.
|
|
|
|
/// Note callers must provide the function name, since the function does not know its own name.
|
|
|
|
wcstring annotated_definition(const wcstring &name) const;
|
2018-02-12 08:37:38 +08:00
|
|
|
};
|
|
|
|
|
2023-04-11 00:27:35 +08:00
|
|
|
// FIXME: Morally, this is const, but cxx doesn't get it
|
|
|
|
using function_properties_ref_t = std::shared_ptr<function_properties_t>;
|
2019-11-13 01:53:10 +08:00
|
|
|
|
2021-10-22 04:23:49 +08:00
|
|
|
/// Add a function. This may mutate \p props to set is_autoload.
|
2021-10-22 04:56:32 +08:00
|
|
|
void function_add(wcstring name, std::shared_ptr<function_properties_t> props);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Remove the function with the specified name.
|
2012-11-19 08:30:30 +08:00
|
|
|
void function_remove(const wcstring &name);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2021-10-22 05:32:35 +08:00
|
|
|
/// \return the properties for a function, or nullptr if none. This does not trigger autoloading.
|
|
|
|
function_properties_ref_t function_get_props(const wcstring &name);
|
2018-02-12 09:21:24 +08:00
|
|
|
|
2023-04-11 00:27:35 +08:00
|
|
|
/// Guff to work around cxx not getting function_properties_t.
|
|
|
|
wcstring function_get_definition_file(const function_properties_t &props);
|
|
|
|
wcstring function_get_copy_definition_file(const function_properties_t &props);
|
|
|
|
bool function_is_copy(const function_properties_t &props);
|
|
|
|
int function_get_definition_lineno(const function_properties_t &props);
|
|
|
|
int function_get_copy_definition_lineno(const function_properties_t &props);
|
2023-04-20 04:39:38 +08:00
|
|
|
wcstring function_get_annotated_definition(const function_properties_t &props,
|
|
|
|
const wcstring &name);
|
2023-04-11 00:27:35 +08:00
|
|
|
|
2021-10-22 05:28:39 +08:00
|
|
|
/// \return the properties for a function, or nullptr if none, perhaps triggering autoloading.
|
|
|
|
function_properties_ref_t function_get_props_autoload(const wcstring &name, parser_t &parser);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2022-06-12 02:34:52 +08:00
|
|
|
/// Try autoloading a function.
|
|
|
|
/// \return true if something new was autoloaded, false if it was already loaded or did not exist.
|
|
|
|
bool function_load(const wcstring &name, parser_t &parser);
|
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Sets the description of the function with the name \c name.
|
2021-10-22 05:28:39 +08:00
|
|
|
/// This triggers autoloading.
|
2019-05-05 11:20:52 +08:00
|
|
|
void function_set_desc(const wcstring &name, const wcstring &desc, parser_t &parser);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2021-10-22 05:28:39 +08:00
|
|
|
/// Returns true if the function named \p cmd exists.
|
2018-02-12 09:21:24 +08:00
|
|
|
/// This may autoload.
|
2021-10-22 05:28:39 +08:00
|
|
|
bool function_exists(const wcstring &cmd, parser_t &parser);
|
2015-10-08 09:59:41 +08:00
|
|
|
|
2021-10-22 05:28:39 +08:00
|
|
|
/// Returns true if the function \p cmd either is loaded, or exists on disk in an autoload
|
|
|
|
/// directory.
|
2021-06-23 03:37:45 +08:00
|
|
|
bool function_exists_no_autoload(const wcstring &cmd);
|
2011-12-27 11:18:46 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Returns all function names.
|
|
|
|
///
|
|
|
|
/// \param get_hidden whether to include hidden functions, i.e. ones starting with an underscore.
|
2023-04-19 06:19:10 +08:00
|
|
|
std::vector<wcstring> function_get_names(bool get_hidden);
|
2005-09-20 21:26:39 +08:00
|
|
|
|
2016-05-01 11:31:02 +08:00
|
|
|
/// Creates a new function using the same definition as the specified function. Returns true if copy
|
|
|
|
/// is successful.
|
2023-02-13 23:59:28 +08:00
|
|
|
bool function_copy(const wcstring &name, const wcstring &new_name, parser_t &parser);
|
2010-09-08 01:31:05 +08:00
|
|
|
|
2018-02-16 13:58:02 +08:00
|
|
|
/// Observes that fish_function_path has changed.
|
|
|
|
void function_invalidate_path();
|
|
|
|
|
2005-10-04 23:11:39 +08:00
|
|
|
#endif
|