diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d4e79a8b..f94aa97c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - python-version: ["3.8", "3.9", "3.10"] + python-version: ["3.7", "3.8", "3.9", "3.10"] fail-fast: false runs-on: ${{ matrix.os }} diff --git a/.gitignore b/.gitignore index 58c5a787..b1e79ded 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc .vscode *.egg-info -dist/ \ No newline at end of file +dist/ +docs/_build/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d2880cd..39397ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # CHANGELONG +## 2.0.0 + +### Adds + +- Adds support for including preprocessor definitions from files same as `pp_defs` +- Adds hover support for preprocessor variables +- Adds Go To Definition for `include` statements +- Adds intrinsic support for `OpenACC` version 3.1 +- Adds sphinx autogenerated documentation +- Adds `incl_suffixes` as a configuration option + +### Changes + +- Update constant parameters for `omp_lib` and `omp_lib_kinds` Interface v5.0 +- Format json files with `prettier` + +### Fixes + +- Fixes the hover of preprocessor functions. It now displays the function name + witout the argument list and the function body. The argument list cannot be + multiline but the function body can. + ## 1.16.0 ### Adds diff --git a/README.md b/README.md index 432cff9d..0322f01f 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) `fortls` is an implementation of the [Language Server Protocol](https://github.com/Microsoft/language-server-protocol) -(LSP) for Fortran using Python (3.6+). +(LSP) for Fortran using Python (3.7+). Editor extensions that can integrate with `fortls` to provide autocomplete and other IDE-like functionality are available for @@ -28,29 +28,30 @@ potentially subject to change. ## Features -- Document symbols (`textDocument/documentSymbol`) -- Auto-complete (`textDocument/completion`) -- Signature help (`textDocument/signatureHelp`) -- GoTo/Peek definition (`textDocument/definition`) -- Hover (`textDocument/hover`) -- GoTo implementation (`textDocument/implementation`) -- Find/Peek references (`textDocument/references`) -- Project-wide symbol search (`workspace/symbol`) +- Project-wide and Document symbol detection and Renaming +- Hover support, Signature help and Auto-completion +- GoTo/Peek implementation and Find/Peek references - Symbol renaming (`textDocument/rename`) - Documentation parsing ([Doxygen](http://www.doxygen.org/) and [FORD](https://github.com/Fortran-FOSS-Programmers/ford) styles) -- Diagnostics (limited) +- Access to multiple intrinsic modules and functions + - `ISO_FORTRAN_ENV` GCC 11.2.0 + - `IOS_C_BINDING` GCC 11.2.0 + - `IEEE_EXCEPTIONS`, `IEEE_ARITHMETIC`, `IEEE_FEATURES` GCC 11.2.0 + - OpenMP `OMP_LIB`, `OMP_LIB_KINDS` v5.0 + - OpenACC `OPENACC`, `OPENACC_KINDS` v3.1 +- Diagnostics - Multiple definitions with the same variable name - Variable definition masks definition from parent scope - Missing subroutine/function arguments - - Unknown user-defined type used in "TYPE"/"CLASS" definition + - Unknown user-defined type used in `TYPE`/`CLASS` definition (only if visible in project) - Unclosed blocks/scopes - Invalid scope nesting - - Unknown modules in "USE" statement + - Unknown modules in `USE` statement - Unimplemented deferred type-bound procedures - Use of unimported variables/objects in interface blocks - - Statement placement errors ("CONTAINS", "IMPLICIT", "IMPORT") + - Statement placement errors (`CONTAINS`, `IMPLICIT`, `IMPORT`) - Code actions (`textDocument/codeAction`) \[Experimental\] - Generate type-bound procedures and implementation templates for deferred procedures @@ -59,6 +60,7 @@ potentially subject to change. - Signature help is not available for overloaded subroutines/functions - Diagnostics are only updated when files are saved or opened/closed +- Files included for preprocessor are not parsed for Fortran objects ## Installation @@ -66,7 +68,7 @@ potentially subject to change. pip install fortls ``` -## fortls settings +## Settings The following global settings can be used when launching the language server. @@ -155,6 +157,7 @@ All command line options are also available through the **options** file as well - `max_line_length` Maximum line length (default: none) - `max_comment_line_length` Maximum comment line length (default: none) +- `incl_suffixes` Add more Fortran extensions to be parsed by the server ## Additional settings @@ -184,6 +187,7 @@ By default all source directories under `root_dir` are recursively included. Source file directories can also be specified manually by specifying their paths in the `source_dirs` variable in the configuration options file. Paths can be absolute or relative to `root_dir`. +`root_dir` does not need to be specified manually as it is always included. When defining `source_dirs` in the configuration options filethe default behaviour (i.e. including all files in all subdirectories under `root_dir`) is overriden. To include them @@ -195,8 +199,6 @@ back again one can do } ``` -> NOTE: `root_dir` does not need to be specified manually as it is always included. - ### Preprocessing **Note:** Preprocessor support is not "complete", see below. For @@ -278,6 +280,19 @@ Diagnostics: ![image](https://raw.githubusercontent.com/gnikit/fortran-language-server/master/images/fortls_diag.png) --> +## Implemented server requests + +| Request | Description | +| ----------------------------- | ------------------------------------------------------ | +| `workspace/symbol` | Get workspace-wide symbols | +| `textDocument/documentSymbol` | Get document symbols e.g. functions, subroutines, etc. | +| `textDocument/completion` | Suggested tab-completion when typing | +| `textDocument/signatureHelp` | Get signature information at a given cursor position | +| `textDocument/definition` | GoTo implementation/Peek implementation | +| `textDocument/references` | Find all/Peek references | +| `textDocument/rename` | Rename a symbol across the workspace | +| `textDocument/codeAction` | **Experimental** Generate code | + ## Acknowledgements This project would not have been possible without the original work of [@hansec](https://github.com/hansec/) diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..aaa17963 --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,25 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SPHINXAPIDOC ?= sphinx-apidoc +PANDOC ?= pandoc +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +modules: + @$(SPHINXAPIDOC) -f -H "Developers' documentations" ../fortls -o . diff --git a/docs/README.md b/docs/README.md new file mode 120000 index 00000000..32d46ee8 --- /dev/null +++ b/docs/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..9845555f --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,94 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +import os +import sys + +sys.path.insert(0, os.path.abspath("..")) + + +# -- Project information ----------------------------------------------------- + +project = "fortls" +copyright = "2021, Giannis Nikiteas" +author = "Giannis Nikiteas" + +# The full version, including alpha/beta/rc tags +release = "1.16.0" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + "sphinx.ext.autodoc", + "sphinx.ext.autosectionlabel", + "sphinx.ext.autosummary", + "sphinx.ext.napoleon", + "sphinx.ext.intersphinx", + "sphinx.ext.inheritance_diagram", + "sphinx_autodoc_typehints", + "sphinx.ext.autosectionlabel", + "myst_parser", +] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] +source_suffix = [".rst", ".md"] + + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "alabaster" +html_theme = "sphinx_rtd_theme" + + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ["_static"] + + +display_toc = True +# autodoc_default_flags = ["members"] +autosummary_generate = True + + +intersphinx_mapping = { + "python": ("https://docs.python.org/3.10", None), +} + +inheritance_graph_attrs = { + "size": '"6.0, 8.0"', + "fontsize": 32, + "bgcolor": "transparent", +} +inheritance_node_attrs = { + "color": "black", + "fillcolor": "white", + "style": '"filled,solid"', +} +inheritance_edge_attrs = { + "penwidth": 1.2, + "arrowsize": 0.8, +} diff --git a/docs/fortls.rst b/docs/fortls.rst new file mode 100644 index 00000000..e8daead1 --- /dev/null +++ b/docs/fortls.rst @@ -0,0 +1,77 @@ +fortls package +============== + +Submodules +---------- + +fortls.constants module +----------------------- + +.. automodule:: fortls.constants + :members: + :undoc-members: + :show-inheritance: + +fortls.helper\_functions module +------------------------------- + +.. automodule:: fortls.helper_functions + :members: + :undoc-members: + :show-inheritance: + +fortls.intrinsics module +------------------------ + +.. automodule:: fortls.intrinsics + :members: + :undoc-members: + :show-inheritance: + +fortls.jsonrpc module +--------------------- + +.. automodule:: fortls.jsonrpc + :members: + :undoc-members: + :show-inheritance: + +fortls.langserver module +------------------------ + +.. automodule:: fortls.langserver + :members: + :undoc-members: + :show-inheritance: + +fortls.objects module +--------------------- + +.. automodule:: fortls.objects + :members: + :undoc-members: + :show-inheritance: + +fortls.parse\_fortran module +---------------------------- + +.. automodule:: fortls.parse_fortran + :members: + :undoc-members: + :show-inheritance: + +fortls.regex\_patterns module +----------------------------- + +.. automodule:: fortls.regex_patterns + :members: + :undoc-members: + :show-inheritance: + +Module contents +--------------- + +.. automodule:: fortls + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..552e86cb --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,26 @@ +.. fortls documentation master file, created by + sphinx-quickstart on Mon Jan 10 11:32:27 2022. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +fortls +================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + README.md + modules.rst + +.. + Include native markdown into native rst + .. include:: README.md + :parser: myst_parser.sphinx_ + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 00000000..8084272b --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 00000000..bf41c9b0 --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,7 @@ +Developers' documentations +========================== + +.. toctree:: + :maxdepth: 4 + + fortls diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 00000000..704cda56 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,6 @@ +docutils +myst-parser +sphinx +sphinx_autodoc_typehints +sphinx_rtd_theme +sphinxprettysearchresults \ No newline at end of file diff --git a/fortls/__init__.py b/fortls/__init__.py index fe73b429..d9469210 100644 --- a/fortls/__init__.py +++ b/fortls/__init__.py @@ -781,10 +781,10 @@ def debug_server_parser(args): print("\nTesting parser") print(' File = "{0}"'.format(args.debug_filepath)) file_obj = fortran_file(args.debug_filepath, pp_suffixes) - err_str = file_obj.load_from_disk() - if err_str is not None: - error_exit("Reading file failed: {0}".format(err_str)) - print(" Detected format: {0}".format("fixed" if file_obj.fixed else "free")) + err_str, _ = file_obj.load_from_disk() + if err_str: + error_exit(f"Reading file failed: {err_str}") + print(f" Detected format: {'fixed' if file_obj.fixed else 'free'}") print("\n=========\nParser Output\n=========\n") _, file_ext = os.path.splitext(os.path.basename(args.debug_filepath)) preproc_file = False @@ -794,10 +794,10 @@ def debug_server_parser(args): preproc_file = file_ext == file_ext.upper() if preproc_file: file_ast = process_file( - file_obj, True, debug=True, pp_defs=pp_defs, include_dirs=include_dirs + file_obj, debug=True, pp_defs=pp_defs, include_dirs=include_dirs ) else: - file_ast = process_file(file_obj, True, debug=True) + file_ast = process_file(file_obj, debug=True) print("\n=========\nObject Tree\n=========\n") for obj in file_ast.get_scopes(): print("{0}: {1}".format(obj.get_type(), obj.FQSN)) diff --git a/fortls/helper_functions.py b/fortls/helper_functions.py index 3ad17f1d..1d28fd1f 100644 --- a/fortls/helper_functions.py +++ b/fortls/helper_functions.py @@ -21,8 +21,21 @@ log = logging.getLogger(__name__) -def expand_name(line, char_poss): - """Get full word containing given cursor position""" +def expand_name(line: str, char_poss: int) -> str: + """Get full word containing given cursor position + + Parameters + ---------- + line : str + Text line + char_poss : int + Column position along the line + + Returns + ------- + str + Word regex match for the input column + """ # The order here is important. # WORD will capture substrings in logical and strings regexs = [LOGICAL_REGEX, SQ_STRING_REGEX, DQ_STRING_REGEX, WORD_REGEX, NUMBER_REGEX] @@ -33,10 +46,21 @@ def expand_name(line, char_poss): return "" -def detect_fixed_format(file_lines): +def detect_fixed_format(file_lines: list[str]) -> bool: """Detect fixed/free format by looking for characters in label columns and variable declarations before column 6. Treat intersection format - files as free format.""" + files as free format. + + Parameters + ---------- + file_lines : list[str] + List of consecutive file lines + + Returns + ------- + bool + True if file_lines are of Fixed Fortran style + """ for line in file_lines: if FREE_FORMAT_TEST.match(line): return False @@ -51,8 +75,20 @@ def detect_fixed_format(file_lines): return True -def strip_line_label(line): - """Strip leading numeric line label""" +def strip_line_label(line: str) -> tuple[str, str | None]: + """Strip leading numeric line label + + Parameters + ---------- + line : str + Text line + + Returns + ------- + tuple[str, str | None] + Output string, Line label returns None if no line label present + """ + match = LINE_LABEL_REGEX.match(line) if match is None: return line, None @@ -62,8 +98,21 @@ def strip_line_label(line): return out_str, line_label -def strip_strings(in_line, maintain_len=False): - """String string literals from code line""" +def strip_strings(in_line: str, maintain_len: bool = False) -> str: + """Strips string literals from code line + + Parameters + ---------- + in_line : str + Text string + maintain_len : bool, optional + Maintain the len(in_line) in the output string, by default False + + Returns + ------- + str + Stripped string + """ def repl_sq(m): return "'{0}'".format(" " * (len(m.group()) - 2)) @@ -80,12 +129,22 @@ def repl_dq(m): return out_line -def separate_def_list(test_str): +def separate_def_list(test_str: str) -> list[str] | None: """Separate definition lists, skipping parenthesis and bracket groups Examples: "var1, var2, var3" -> ["var1", "var2", "var3"] "var, init_var(3) = [1,2,3], array(3,3)" -> ["var", "init_var", "array"] + + Parameters + ---------- + test_str : str + Text string + + Returns + ------- + list[str] | None + [description] """ stripped_str = strip_strings(test_str) paren_count = 0 @@ -111,8 +170,21 @@ def separate_def_list(test_str): return def_list -def find_word_in_line(line, word): - """Find Fortran word in line""" +def find_word_in_line(line: str, word: str) -> tuple[int, int]: + """Find Fortran word in line + + Parameters + ---------- + line : str + Text line + word : str + word to find in line + + Returns + ------- + tuple[int, int] + start and end positions (indices) of the word if not found it returns + -1, len(word) -1""" i0 = -1 for poss_name in WORD_REGEX.finditer(line): if poss_name.group() == word: @@ -121,9 +193,20 @@ def find_word_in_line(line, word): return i0, i0 + len(word) -def find_paren_match(test_str): +def find_paren_match(test_str: str) -> int: """Find matching closing parenthesis by searching forward, - returns -1 if no match is found""" + returns -1 if no match is found + + Parameters + ---------- + test_str : str + Input string + + Returns + ------- + int + The index of the matching `)` character in the string + """ paren_count = 1 ind = -1 for (i, char) in enumerate(test_str): diff --git a/fortls/intrinsic_funs.json b/fortls/intrinsic_funs.json index 89e8510d..b44ab196 100644 --- a/fortls/intrinsic_funs.json +++ b/fortls/intrinsic_funs.json @@ -23,6 +23,11 @@ "doc": "ACOS(X) computes the arccosine of X (inverse of COS(X)).", "type": 3 }, + "ACOSD": { + "args": "X", + "doc": "ACOSD(X) computes the arccosine of X in degrees (inverse of COSD(X).", + "type": 3 + }, "ACOSH": { "args": "X", "doc": "ACOSH(X) computes the inverse hyperbolic cosine of X.", @@ -48,6 +53,11 @@ "doc": "AINT(A,KIND=kind) truncates its argument to a whole number.", "type": 3 }, + "ALARM": { + "args": "SECONDS,HANDLER,STATUS=status", + "doc": "ALARM(SECONDS,HANDLER,STATUS=status) causes external subroutine HANDLER to be executed after a delay of SECONDS by using alarm(2) to set up a signal and signal(2) to catch it. If STATUS is supplied, it will be returned with the number of seconds remaining until any previously scheduled alarm was due to be delivered, or zero if there was no previously scheduled alarm.", + "type": 2 + }, "ALL": { "args": "MASK,DIM=dim", "doc": "ALL(MASK,DIM=dim) determines if all the values are true in MASK in the array along dimension DIM.", @@ -73,6 +83,11 @@ "doc": "ASIN(X) computes the arcsine of X (inverse of SIN(X)).", "type": 3 }, + "ASIND": { + "args": "X", + "doc": "ASIND(X) computes the arcsine of its X in degrees (inverse of SIND(X)).", + "type": 3 + }, "ASINH": { "args": "X", "doc": "ASINH(X) computes the inverse hyperbolic sine of X.", @@ -88,16 +103,86 @@ "doc": "ATAN(X) computes the arctangent of X (inverse of TAN(X)).", "type": 3 }, + "ATAND": { + "args": "X", + "doc": "ATAND(X) computes the arctangent of X in degrees (inverse of TAND).", + "type": 3 + }, "ATAN2": { "args": "Y,X", "doc": "ATAN2(Y,X) computes the principal value of the argument function of the complex number X + i Y.", "type": 3 }, + "ATAN2D": { + "args": "Y,X", + "doc": "ATAN2D(Y,X) computes the principal value of the argument function of the complex number X + i Y in degrees.", + "type": 3 + }, "ATANH": { "args": "X", "doc": "ATANH(X) computes the inverse hyperbolic tangent of X.", "type": 3 }, + "ATOMIC_ADD": { + "args": "ATOM,VALUE", + "doc": "ATOMIC_ADD(ATOM,VALUE) atomically adds the value of VALUE to the variable ATOM.", + "type": 2 + }, + "ATOMIC_AND": { + "args": "ATOM,VALUE", + "doc": "ATOMIC_AND(ATOM,VALUE) atomically defines ATOM with the bitwise AND between the values of ATOM and VALUE.", + "type": 2 + }, + "ATOMIC_CAS": { + "args": "ATOM,OLD,COMPARE,NEW,STAT=stat", + "doc": "ATOMIC_CAS compares the variable ATOM with the value of COMPARE; if the value is the same, ATOM is set to the value of NEW. Additionally, OLD is set to the value of ATOM that was used for the comparison.", + "type": 2 + }, + "ATOMIC_DEFINE": { + "args": "ATOM,VALUE,STAT=stat", + "doc": "ATOMIC_DEFINE(ATOM,VALUE) defines the variable ATOM with the value VALUE atomically.", + "type": 2 + }, + "ATOMIC_FETCH_ADD": { + "args": "ATOM,VALUE,OLD,STAT=stat", + "doc": "ATOMIC_FETCH_ADD(ATOM,VALUE,OLD) atomically stores the value of ATOM in OLD and adds the value of VALUE to the variable ATOM.", + "type": 2 + }, + "ATOMIC_FETCH_AND": { + "args": "ATOM,VALUE,OLD,STAT=stat", + "doc": "ATOMIC_AND(ATOM,VALUE) atomically stores the value of ATOM in OLD and defines ATOM with the bitwise AND between the values of ATOM and VALUE.", + "type": 2 + }, + "ATOMIC_FETCH_OR": { + "args": "ATOM,VALUE,OLD,STAT=stat", + "doc": "ATOMIC_OR(ATOM,VALUE) atomically stores the value of ATOM in OLD and defines ATOM with the bitwise OR between the values of ATOM and VALUE.", + "type": 2 + }, + "ATOMIC_FETCH_XOR": { + "args": "ATOM,VALUE,OLD,STAT=stat", + "doc": "ATOMIC_XOR(ATOM,VALUE) atomically stores the value of ATOM in OLD and defines ATOM with the bitwise XOR between the values of ATOM and VALUE.", + "type": 2 + }, + "ATOMIC_OR": { + "args": "ATOM,VALUE,STAT=stat", + "doc": "ATOMIC_OR(ATOM,VALUE) atomically defines ATOM with the bitwise AND between the values of ATOM and VALUE.", + "type": 2 + }, + "ATOMIC_REF": { + "args": "ATOM,VALUE,STAT=stat", + "doc": "ATOMIC_DEFINE(ATOM,VALUE) atomically assigns the value of the variable ATOM to VALUE.", + "type": 2 + }, + "ATOMIC_XOR": { + "args": "ATOM,VALUE,STAT=stat", + "doc": "ATOMIC_AND(ATOM,VALUE) atomically defines ATOM with the bitwise XOR between the values of ATOM and VALUE.", + "type": 2 + }, + "BACKTRACE": { + "args": "", + "doc": "BACKTRACE shows a backtrace at an arbitrary place in user code. Program execution continues normally afterwards. The backtrace information is printed to the unit corresponding to ERROR_UNIT in ISO_FORTRAN_ENV.", + "type": 2 + }, "BESSEL_J0": { "args": "X", "doc": "BESSEL_J0(X) computes the Bessel function of the first kind of order 0 of X.", @@ -168,16 +253,56 @@ "doc": "CHAR(I,KIND=kind) returns the character represented by the integer I.", "type": 3 }, + "CHDIR": { + "args": "NAME,STATUS=status", + "doc": "CHDIR(NAME,STATUS=status) change current working directory to a specified path.", + "type": 2 + }, + "CHMOD": { + "args": "NAME,MODE,STATUS=status", + "doc": "CHMOD(NAME,MODE,STATUS=status) changes the permissions of a file.", + "type": 2 + }, "CMPLX": { "args": "X,Y=y,KIND=kind", "doc": "CMPLX(X,Y=y,KIND=kind) returns a complex number where X is converted to the real component.", "type": 3 }, + "CO_BROADCAST": { + "args": "A,SOURCE_IMAGE,STAT=stat,ERRMSG=errmsg", + "doc": "CO_BROADCAST(A,SOURCE_IMAGE,STAT=stat,ERRMSG=errmsg) copies the value of argument A on the image with image index SOURCE_IMAGE to all images in the current team.", + "type": 2 + }, + "CO_MAX": { + "args": "A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg", + "doc": "CO_MAX(A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg) determines element-wise the maximal value of A on all images of the current team.", + "type": 2 + }, + "CO_MIN": { + "args": "A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg", + "doc": "CO_MIN(A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg) determines element-wise the minimal value of A on all images of the current team.", + "type": 2 + }, + "CO_REDUCE": { + "args": "A,OPERATION,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg", + "doc": "CO_REDUCE(A,OPERATION,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg) determines element-wise the reduction of the value of A on all images of the current team.", + "type": 2 + }, + "CO_SUM": { + "args": "A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg", + "doc": "CO_SUM(A,RESULT_IMAGE=result_image,STAT=stat,ERRMSG=errmsg) sums up the values of each element of A on all images of the current team.", + "type": 2 + }, "COMMAND_ARGUMENT_COUNT": { "args": "X", "doc": "COMMAND_ARGUMENT_COUNT() returns the number of arguments passed on the command line when the containing program was invoked.", "type": 3 }, + "COMPLEX": { + "args": "X,Y", + "doc": "COMPLEX(X,Y) returns a complex number where X is converted to the real component and Y is converted to the imaginary component.", + "type": 3 + }, "CONJG": { "args": "Z", "doc": "CONJG(Z) returns the conjugate of Z.", @@ -188,6 +313,11 @@ "doc": "COS(X) computes the cosine of X.", "type": 3 }, + "COSD": { + "args": "X", + "doc": "COSD(X) computes the cosine of X in degrees.", + "type": 3 + }, "COSH": { "args": "X", "doc": "COSH(X) computes the hyperbolic cosine of X.", @@ -198,6 +328,11 @@ "doc": "COTAN(X) computes the cotangent of X.", "type": 3 }, + "COTAND": { + "args": "X", + "doc": "COTAND(X) computes the cotangent of X in degrees.", + "type": 3 + }, "COUNT": { "args": "MASK,DIM=dim,KIND=kind", "doc": "COUNT(MASK,DIM=dim,KIND=kind) Count the number of true elements of MASK along dimension DIM.", @@ -213,6 +348,11 @@ "doc": "CSHIFT(ARRAY,SHIFT,DIM=dim) performs a circular shift on elements of ARRAY along the dimension of DIM.", "type": 3 }, + "CTIME": { + "args": "TIME", + "doc": "CTIME(TIME) converts a system time value, such as returned by TIME8, to a string. The output will be of the form ‘Sat Aug 19 18:13:14 1995’.", + "type": 3 + }, "DATE_AND_TIME": { "args": "DATE,TIME,ZONE,VALUES", "doc": "DATE_AND_TIME(DATE,TIME,ZONE,VALUES) gets the corresponding date and time information from the real-time system clock.", @@ -278,11 +418,26 @@ "doc": "ERFC_SCALED(X) computes the exponentially-scaled complementary error function of X.", "type": 3 }, + "ETIME": { + "args": "VALUES(2),TIME,", + "doc": "ETIME(VALUES(2),TIME) returns the number of seconds of runtime since the start of the process’s execution in TIME.", + "type": 3 + }, + "EVENT_QUERY": { + "args": "EVENT,COUNT,STAT=stat", + "doc": "EVENT_QUERY(EVENT,COUNT,STAT=stat) assigns the number of events to COUNT which have been posted to the EVENT variable and not yet been removed by calling EVENT WAIT.", + "type": 2 + }, "EXECUTE_COMMAND_LINE": { "args": "COMMAND,WAIT=wait,EXITSTAT=exitstat,CMDSTAT=cmdstat,CMDMSG=cmdmsg", "doc": "EXECUTE_COMMAND_LINE(COMMAND,WAIT=wait,EXITSTAT=exitstat,CMDSTAT=cmdstat,CMDMSG=cmdmsg) runs a shell command, synchronously or asynchronously.", "type": 2 }, + "EXIT": { + "args": "STATUS=status", + "doc": "EXIT(STATUS=status) causes immediate termination of the program with status.", + "type": 2 + }, "EXP": { "args": "X", "doc": "EXP(X) computes the base e exponential of X.", @@ -298,21 +453,91 @@ "doc": "EXTENDS_TYPE_OF(A,MOLD) queries dynamic type for extension.", "type": 3 }, + "FDATE": { + "args": "DATE", + "doc": "FDATE(DATE) returns the current date (using the same format as CTIME) in DATE. It is equivalent to CALL CTIME(DATE, TIME()).", + "type": 2 + }, + "FGET": { + "args": "C,STATUS=status", + "doc": "FDATE(C,STATUS=status) Read a single character in stream mode from stdin by bypassing normal formatted output.", + "type": 2 + }, + "FGETC": { + "args": "UNIT,C,STATUS=status", + "doc": "FDATE(UNIT,C,STATUS=status) Read a single character in stream mode by bypassing normal formatted output.", + "type": 2 + }, + "FINDLOC": { + "args": "ARRAY,VALUE,DIM=dim,MASK=mask,KIND=kind,BACK=back", + "doc": "FINDLOC(ARRAY,VALUE,DIM=dim,MASK=mask,KIND=kind,BACK=back) determines the location of the element in the array with the value given in the VALUE argument.", + "type": 3 + }, "FLOOR": { "args": "A,KIND=kind", "doc": "FLOOR(A,KIND=kind) returns the greatest integer less than or equal to A.", "type": 3 }, + "FLUSH": { + "args": "UNIT=unit", + "doc": "FLUSH(UNIT=unit) Flushes Fortran unit(s) currently open for output.", + "type": 2 + }, + "FNUM": { + "args": "UNIT", + "doc": "FNUM(UNIT) returns the POSIX file descriptor number corresponding to the open Fortran I/O unit UNIT.", + "type": 3 + }, + "FPUT": { + "args": "C,STATUS=status", + "doc": "FPUT(C,STATUS=status) Write a single character in stream mode to stdout by bypassing normal formatted output.", + "type": 3 + }, + "FPUTC": { + "args": "C,UNIT=unit,STATUS=status", + "doc": "FPUTC(C,UNIT=unit,STATUS=status) Write a single character in stream mode to stdout by bypassing normal formatted output.", + "type": 3 + }, "FRACTION": { "args": "X", "doc": "FRACTION(X) returns the fractional part of the model representation of X.", "type": 3 }, + "FREE": { + "args": "PTR", + "doc": "FREE(PTR) Frees memory previously allocated by MALLOC.", + "type": 2 + }, + "FSEEK": { + "args": "UNIT,OFFSET,WHENCE,STATUS=status", + "doc": "FSEEK(UNIT,OFFSET,WHENCE,STATUS=status) Moves UNIT to the specified OFFSET.", + "type": 2 + }, + "FSTAT": { + "args": "UNIT,VALUES", + "doc": "FSTAT(UNIT,VALUES) FSTAT is identical to STAT, except that information about an already opened file is obtained.", + "type": 3 + }, + "FTELL": { + "args": "UNIT", + "doc": "FSTAT(UNIT) Retrieves the current position within an open file.", + "type": 3 + }, "GAMMA": { "args": "X", "doc": "GAMMA(X) computes the gamma function of X.", "type": 3 }, + "GERROR": { + "args": "RESULT", + "doc": "GERROR(RESULT) Returns the system error message corresponding to the last system error.", + "type": 2 + }, + "GETARG": { + "args": "POS,VALUE", + "doc": "GETARG(POS,VALUE) Retrieve the POS-th argument that was passed on the command line when the containing program was invoked.", + "type": 2 + }, "GET_COMMAND": { "args": "COMMAND=command,LENGTH=length,STATUS=status", "doc": "GET_COMMAND(COMMAND=command,LENGTH=length,STATUS=status) retrieve the entire command line that was used to invoke the program.", @@ -323,11 +548,51 @@ "doc": "GET_COMMAND_ARGUMENT(NUMBER=number,VALUE=value,LENGTH=length,STATUS=status) retrieve the NUMBER-th argument that was passed on the command line when the containing program was invoked.", "type": 2 }, + "GETCWD": { + "args": "C,STATUS=status", + "doc": "GETCWD(C,STATUS=status) Get current working directory.", + "type": 3 + }, + "GETENV": { + "args": "NAME,VALUE", + "doc": "GETENV(NAME,VALUE) Get the VALUE of the environmental variable NAME.", + "type": 2 + }, "GET_ENVIRONMENT_VARIABLE": { "args": "NAME=name,VALUE=value,LENGTH=length,STATUS=status,TRIM_NAME=trim_name", "doc": "GET_ENVIRONMENT_VARIABLE(NAME=name,VALUE=value,LENGTH=length,STATUS=status,TRIM_NAME=trim_name) gets the VALUE of the environmental variable NAME.", "type": 2 }, + "GETGID": { + "args": "", + "doc": "GETGID() Returns the numerical group ID of the current process.", + "type": 3 + }, + "GETLOG": { + "args": "C", + "doc": "GETLOG(C)Gets the username under which the program is running.", + "type": 2 + }, + "GETPID": { + "args": "", + "doc": "GETPID() Returns the numerical process identifier of the current process.", + "type": 3 + }, + "GETUID": { + "args": "", + "doc": "GETUID() Returns the numerical user ID of the current process.", + "type": 3 + }, + "GMTIME": { + "args": "TIME,VALUES", + "doc": "GMTIME(TIME,VALUES) Given a system time value TIME (as provided by the TIME intrinsic), fills VALUES with values extracted from it appropriate to the UTC time zone, using gmtime(3).", + "type": 2 + }, + "HOSTNM": { + "args": "C,STATUS=status", + "doc": "HOSTNM(C,STATUS=status) Retrieves the host name of the system on which the program is running.", + "type": 3 + }, "HUGE": { "args": "X", "doc": "HUGE(X) returns the largest number that is not an infinity in the model of the type of X.", @@ -358,6 +623,11 @@ "doc": "IANY(MASK,DIM=dim) reduces with bitwise OR the elements of ARRAY along dimension DIM.", "type": 3 }, + "IARGC": { + "args": "", + "doc": "IARGC() returns the number of arguments passed on the command line when the containing program was invoked.", + "type": 3 + }, "IBCLR": { "args": "I,POS", "doc": "IBCLR(I,POS) returns the value of I with the bit at position POS set to zero.", @@ -378,11 +648,21 @@ "doc": "ICHAR(C,KIND=kind) returns the code for the character in the first character position of C in the system's native character set.", "type": 3 }, + "IDATE": { + "args": "VALUES", + "doc": "IDATE(VALUES) Fills VALUES with the numerical values at the current local time.", + "type": 2 + }, "IEOR": { "args": "I,J", "doc": "IEOR(I,J) Bitwise logical exclusive OR.", "type": 3 }, + "IEORNO": { + "args": "", + "doc": "IEORNO() Returns the last system error number, as given by the C errno variable.", + "type": 3 + }, "IMAGE_INDEX": { "args": "COARRAY,SUB", "doc": "IMAGE_INDEX(COARRAY,SUB) returns the image index belonging to a cosubscript.", @@ -393,6 +673,21 @@ "doc": "INDEX(STRING,SUBSTRING,BACK=back,KIND=kind) returns the position of the start of the first occurrence of string SUBSTRING as a substring in STRING, counting from one.", "type": 3 }, + "INT": { + "args": "A,KIND=kind", + "doc": "INT(A,KIND=kind) Convert to integer type.", + "type": 3 + }, + "INT2": { + "args": "A", + "doc": "INT2(A) Convert to a KIND=2 integer type.", + "type": 3 + }, + "INT8": { + "args": "A", + "doc": "INT8(A) Convert to a KIND=8 integer type.", + "type": 3 + }, "IOR": { "args": "I,J", "doc": "IOR(I,J) Bitwise logical inclusive OR.", @@ -403,14 +698,14 @@ "doc": "IPARITY(ARRAY,DIM=dim,MASK=mask) reduces with bitwise XOR (exclusive or) the elements of ARRAY along dimension DIM if the corresponding element in MASK is TRUE.", "type": 3 }, - "ISHFT": { - "args": "I,SHIFT", - "doc": "ISHFT(I,SHIFT) returns a value corresponding to I with all of the bits shifted SHIFT places", + "IRAND": { + "args": "FLAG", + "doc": "IRAND(FLAG) returns a pseudo-random number from a uniform distribution between 0 and a system-dependent limit (which is in most cases 2147483647).", "type": 3 }, - "ISHFTC": { - "args": "I,SHIFT,SIZE=size", - "doc": "ISHFTC(I,SHIFT,SIZE=size) returns a value corresponding to I with the rightmost SIZE bits shifted circularly SHIFT places; that is, bits shifted out one end are shifted into the opposite end.", + "IS_CONTIGUOUS": { + "args": "ARRAY", + "doc": "IS_CONTIGUOUS(ARRAY) tests whether an array is contiguous.", "type": 3 }, "IS_IOSTAT_END": { @@ -423,6 +718,36 @@ "doc": "IS_IOSTAT_EOR(I) tests whether the variable I has the value of the I/O status 'end of record'", "type": 3 }, + "ISATTY": { + "args": "UNIT", + "doc": "ISATTY(UNIT) Determine whether a unit is connected to a terminal device.", + "type": 3 + }, + "ISHFT": { + "args": "I,SHIFT", + "doc": "ISHFT(I,SHIFT) returns a value corresponding to I with all of the bits shifted SHIFT places.", + "type": 3 + }, + "ISHFTC": { + "args": "I,SHIFT,SIZE=size", + "doc": "ISHFTC(I,SHIFT,SIZE=size) returns a value corresponding to I with the rightmost SIZE bits shifted circularly SHIFT places; that is, bits shifted out one end are shifted into the opposite end.", + "type": 3 + }, + "ISNAN": { + "args": "X", + "doc": "ISNAN(X) tests whether a floating-point value is an IEEE Not-a-Number (NaN).", + "type": 3 + }, + "ITIME": { + "args": "VALUES", + "doc": "ITIME(VALUES) Fills VALUES with the numerical values at the current local time.", + "type": 2 + }, + "KILL": { + "args": "PID,STATUS=status", + "doc": "KILL(PID,STATUS=status) Sends the signal specified by SIG to the process PID. See kill(2).", + "type": 3 + }, "KIND": { "args": "X", "doc": "KIND(X) returns the kind value of the entity X.", @@ -433,6 +758,16 @@ "doc": "LBOUND(ARRAY,DIM=dim,KIND=kind) returns the lower bounds of an array, or a single lower bound along the DIM dimension.", "type": 3 }, + "LCOBOUND": { + "args": "COARRAY,DIM=dim,KIND=kind", + "doc": "LCOBOUND(COARRAY,DIM=dim,KIND=kind) Returns the lower bounds of a coarray, or a single lower cobound along the DIM codimension.", + "type": 3 + }, + "LEADZ": { + "args": "I", + "doc": "LEADZ(I) returns the number of leading zero bits of an integer.", + "type": 3 + }, "LEN": { "args": "STRING,KIND=kind", "doc": "LEN(STRING,KIND=kind) returns the length of a character string.", @@ -453,6 +788,11 @@ "doc": "LGT(STRING_A,STRING_B) determines whether one string is lexically greater than another string.", "type": 3 }, + "LINK": { + "args": "PATH1,PATH2", + "doc": "LINK(PATH1,PATH2) Makes a (hard) link from file PATH1 to PATH2.", + "type": 3 + }, "LLE": { "args": "STRING_A,STRING_B", "doc": "LLE(STRING_A,STRING_B) determines whether one string is lexically less than or equal to another string.", @@ -463,6 +803,16 @@ "doc": "LLT(STRING_A,STRING_B) determines whether one string is lexically less than another string.", "type": 3 }, + "LNBLNK": { + "args": "STRING", + "doc": "LNBLNK(STRING) Returns the length of a character string, ignoring any trailing blanks.", + "type": 3 + }, + "LOC": { + "args": "X", + "doc": "LOC(X) returns the address of X as an integer.", + "type": 3 + }, "LOG": { "args": "X", "doc": "LOG(X) computes the natural logarithm of X, i.e. the logarithm to the base e.", @@ -473,6 +823,46 @@ "doc": "LOG10(X) computes the base 10 logarithm of X.", "type": 3 }, + "LOG_GAMMA": { + "args": "X", + "doc": "LOG_GAMMA(X) computes the natural logarithm of the absolute value of the Gamma function.", + "type": 3 + }, + "LOGICAL": { + "args": "L,KIND=kind", + "doc": "LOGICAL(L,KIND=kind) Converts one kind of LOGICAL variable to another.", + "type": 3 + }, + "LSHIFT": { + "args": "I,SHIFT", + "doc": "LSHIFT(I,SHIFT) returns a value corresponding to I with all of the bits shifted left by SHIFT places.", + "type": 3 + }, + "LSTAT": { + "args": "NAME,VALUES,STATUS=status", + "doc": "LSTAT(NAME,VALUES,STATUS=status) is identical to STAT, except that if path is a symbolic link, then the link itself is statted, not the file that it refers to.", + "type": 3 + }, + "LTIME": { + "args": "TIME,VALUES", + "doc": "LTIME(TIME,VALUES) Given a system time value TIME (as provided by the TIME intrinsic), fills VALUES with values extracted from it appropriate to the local time zone using localtime(3).", + "type": 2 + }, + "MALLOC": { + "args": "SIZE", + "doc": "MALLOC(SIZE) allocates SIZE bytes of dynamic memory and returns the address of the allocated memory.", + "type": 3 + }, + "MASKL": { + "args": "I,KIND=kind", + "doc": "MASKL(I,KIND=kind) has its leftmost I bits set to 1, and the remaining bits set to 0.", + "type": 3 + }, + "MASKR": { + "args": "I,KIND=kind", + "doc": "MASKR(I,KIND=kind) has its rightmost I bits set to 1, and the remaining bits set to 0.", + "type": 3 + }, "MATMUL": { "args": "MATRIX_A,MATRIX_B", "doc": "MATMUL(MATRIX_A,MATRIX_B) performs a matrix multiplication on numeric or logical arguments.", @@ -498,11 +888,26 @@ "doc": "MAXVAL(ARRAY,DIM=dim,MASK=mask) determines the maximum value of the elements in an array.", "type": 3 }, + "MCLOCK": { + "args": "", + "doc": "MCLOCK() Returns the number of clock ticks since the start of the process, based on the function clock(3) in the C standard library.", + "type": 3 + }, + "MCLOCK8": { + "args": "", + "doc": "MCLOCK8() Returns the number of clock ticks since the start of the process, based on the function clock(3) in the C standard library.", + "type": 3 + }, "MERGE": { "args": "TSOURCE,FSOURCE,MASK", "doc": "MERGE(TSOURCE,FSOURCE,MASK) select values from two arrays according to a logical mask.", "type": 3 }, + "MERGE_BITS": { + "args": "I,J,MASK", + "doc": "MERGE_BITS(I,J,MASK) merges the bits of I and J as determined by the mask.", + "type": 3 + }, "MIN": { "args": "A1,A2", "doc": "MIN(A1,A2,...) returns the argument with the smallest (most negative) value.", @@ -558,6 +963,11 @@ "doc": "NINT(A,KIND=kind) rounds its argument to the nearest whole number.", "type": 3 }, + "NORM2": { + "args": "ARRAY,DIM=dim", + "doc": "NORM2(ARRAY,DIM=dim) Calculates the Euclidean vector norm (L_2 norm) of ARRAY along dimension DIM.", + "type": 3 + }, "NOT": { "args": "I", "doc": "NOT(I) returns the bitwise Boolean inverse of I.", @@ -568,8 +978,28 @@ "type": 3 }, "PACK": { - "args": "A,KIND=kind", - "doc": "PACK(ARRAY,MASK,VECTOR=vector) stores the elements of ARRAY in an array of rank one.", + "args": "ARRAY,MASK=mask,VECTOR=vector", + "doc": "PACK(ARRAY,MASK=mask,VECTOR=vector) stores the elements of ARRAY in an array of rank one.", + "type": 3 + }, + "PARITY": { + "args": "MASK,DIM=dim", + "doc": "PARITY(MASK,DIM=dim) Calculates the parity, i.e. the reduction using .XOR., of MASK along dimension DIM.", + "type": 3 + }, + "PERROR": { + "args": "STRING", + "doc": "PERROR(STRING) Prints (on the C stderr stream) a newline-terminated error message corresponding to the last system error. This is prefixed by STRING, a colon and a space.", + "type": 2 + }, + "POPCNT": { + "args": "I", + "doc": "POPCNT(I) returns the number of bits set (’1’ bits) in the binary representation of I.", + "type": 3 + }, + "POPPAR": { + "args": "I", + "doc": "POPPAR(I) returns parity of the integer I, i.e. the parity of the number of bits set ('1' bits) in the binary representation of I. It is equal to 0 if I has an even number of bits set, and 1 for an odd number of '1' bits.", "type": 3 }, "PRECISION": { @@ -592,6 +1022,21 @@ "doc": "RADIX(X) returns the base of the model representing the entity X.", "type": 3 }, + "RAN": { + "args": "I", + "doc": "RAN(I) For compatibility with HP FORTRAN 77/iX, the RAN intrinsic is provided as an alias for RAND.", + "type": 3 + }, + "RAND": { + "args": "I", + "doc": "RAND(I) returns a pseudo-random number from a uniform distribution between 0 and 1.", + "type": 3 + }, + "RANDOM_INIT": { + "args": "REPEATABLE,IMAGE_DISTINCT", + "doc": "RANDOM_INIT(REPEATABLE,IMAGE_DISTINCT) Initializes the state of the pseudorandom number generator used by RANDOM_NUMBER.", + "type": 2 + }, "RANDOM_NUMBER": { "args": "HARVEST", "doc": "RANDOM_NUMBER(HARVEST) returns a single pseudorandom number or an array of pseudorandom numbers.", @@ -607,6 +1052,21 @@ "doc": "RANGE(X) returns the decimal exponent range in the model of the type of X.", "type": 3 }, + "RANK": { + "args": "A", + "doc": "RANK(A) returns the rank of a scalar or array data object.", + "type": 3 + }, + "REAL": { + "args": "A,KIND=kind", + "doc": "REAL(A,KIND=kind) converts its argument A to a real type.", + "type": 3 + }, + "RENAME": { + "args": "PATH1,PATH2", + "doc": "RENAME(PATH1,PATH2) Renames a file from file PATH1 to PATH2.", + "type": 3 + }, "REPEAT": { "args": "STRING,NCOPIES", "doc": "REPEAT(STRING,NCOPIES) concatenates NCOPIES copies of a string.", @@ -622,6 +1082,11 @@ "doc": "RRSPACING(X) returns the reciprocal of the relative spacing of model numbers near X.", "type": 3 }, + "RSHIFT": { + "args": "I,SHIFT", + "doc": "RSHIFT(I,SHIFT) eturns a value corresponding to I with all of the bits shifted right by SHIFT places.", + "type": 3 + }, "SAME_TYPE_AS": { "args": "A,B", "doc": "SAME_TYPE_AS(A,B) query dynamic types for equality.", @@ -633,10 +1098,20 @@ "type": 3 }, "SCAN": { - "args": "SOURCE,KIND=kind", + "args": "STRING,SET,BACK=back,KIND=kind", "doc": "SCAN(STRING,SET,BACK=back,KIND=kind) scans a STRING for any of the characters in a SET of characters.", "type": 3 }, + "SECNDS": { + "args": "X", + "doc": "SECNDS(X) gets the time in seconds from the real-time system clock.", + "type": 3 + }, + "SECOND": { + "args": "TIME", + "doc": "SECOND(TIME) Returns a REAL(4) value representing the elapsed CPU time in seconds.", + "type": 3 + }, "SELECTED_CHAR_KIND": { "args": "NAME", "doc": "SELECTED_CHAR_KIND(NAME) returns the kind value for the character set named NAME, if a character set with such a name is supported, or -1 otherwise.", @@ -662,16 +1137,41 @@ "doc": "SHAPE(SOURCE,KIND=kind) determines the shape of an array.", "type": 3 }, + "SHIFTA": { + "args": "I,SHIFT", + "doc": "SHIFTA(I,SHIFT) returns a value corresponding to I with all of the bits shifted right by SHIFT places.", + "type": 3 + }, + "SHIFTL": { + "args": "I,SHIFT", + "doc": "SHIFTL(I,SHIFT) returns a value corresponding to I with all of the bits shifted left by SHIFT places.", + "type": 3 + }, + "SHIFTR": { + "args": "I,SHIFT", + "doc": "SHIFTR(I,SHIFT) returns a value corresponding to I with all of the bits shifted right by SHIFT places.", + "type": 3 + }, "SIGN": { "args": "A,B", "doc": "SIGN(A,B) returns the value of A with the sign of B.", "type": 3 }, + "SIGNAL": { + "args": "NUMBER,HANDLER", + "doc": "SIGNAL(NUMBER,HANDLER) causes external subroutine HANDLER to be executed with a single integer argument when signal NUMBER occurs.", + "type": 3 + }, "SIN": { "args": "X", "doc": "SIN(X) computes the sine of X.", "type": 3 }, + "SIND": { + "args": "X", + "doc": "SIND(X) computes the sine of X in degrees.", + "type": 3 + }, "SINH": { "args": "X", "doc": "SINH(X) computes the hyperbolic sine of X.", @@ -682,6 +1182,16 @@ "doc": "SIZE(ARRAY,DIM=dim,KIND=kind) determines the extent of ARRAY along a specified dimension DIM, or the total number of elements in ARRAY if DIM is absent.", "type": 3 }, + "SIZEOF": { + "args": "X", + "doc": "SIZEOF(X) calculates the number of bytes of storage the expression X occupies.", + "type": 3 + }, + "SLEEP": { + "args": "SECONDS", + "doc": "SLEEP(SECONDS) Calling this subroutine causes the process to pause for SECONDS seconds.", + "type": 2 + }, "SPACING": { "args": "X", "doc": "SPACING(X) determines the distance between the argument X and the nearest adjacent number of the same type.", @@ -697,11 +1207,36 @@ "doc": "SQRT(X) computes the square root of X.", "type": 3 }, + "SRAND": { + "args": "SEED", + "doc": "SRAND(SEED) reinitializes the pseudo-random number generator called by RAND and IRAND.", + "type": 2 + }, + "STAT": { + "args": "NAME,VALUES", + "doc": "STAT(NAME,VALUES) This function returns information about a file.", + "type": 3 + }, + "STORAGE_SIZE": { + "args": "A,KIND=kind", + "doc": "STORAGE_SIZE(A,KIND=kind) Returns the storage size of argument A in bits.", + "type": 3 + }, "SUM": { "args": "ARRAY,DIM=dim,MASK=mask", "doc": "SUM(ARRAY,DIM=dim,MASK=mask) adds the elements of ARRAY along dimension DIM if the corresponding element in MASK is TRUE.", "type": 3 }, + "SYMLNK": { + "args": "PATH1,PATH2", + "doc": "SYMLNK(PATH1,PATH2) Makes a symbolic link from file PATH1 to PATH2.", + "type": 3 + }, + "SYSTEM": { + "args": "COMMAND,STATUS=status", + "doc": "SYSTEM(COMMAND,STATUS=status) Passes the command COMMAND to a shell (see system(3)).", + "type": 3 + }, "SYSTEM_CLOCK": { "args": "COUNT=count,COUNT_RATE=count_rate,COUNT_MAX=count_max", "doc": "SYSTEM_CLOCK(COUNT=count,COUNT_RATE=count_rate,COUNT_MAX=count_max) determines the COUNT of a processor clock since an unspecified time in the past modulo COUNT_MAX, COUNT_RATE determines the number of clock ticks per second.", @@ -712,16 +1247,41 @@ "doc": "TAN(X) computes the tangent of X.", "type": 3 }, + "TAND": { + "args": "X", + "doc": "TAND(X) computes the tangent of X in degrees.", + "type": 3 + }, "TANH": { "args": "X", "doc": "TANH(X) computes the hyperbolic tangent of X.", "type": 3 }, + "THIS_IMAGE": { + "args": "DISTANCE=distance|COARRAY,DIM=dim", + "doc": "THIS_IMAGE(DISTANCE=distance|COARRAY,DIM=dim) Returns the cosubscript for this image.", + "type": 3 + }, + "TIME": { + "args": "", + "doc": "TIME() Returns the current time encoded as an integer.", + "type": 3 + }, + "TIME8": { + "args": "", + "doc": "TIME8() Returns the current time encoded as an integer. This value is suitable for passing to CTIME, GMTIME, and LTIME.", + "type": 3 + }, "TINY": { "args": "X", "doc": "TINY(X) returns the smallest positive (non zero) number in the model of the type of X.", "type": 3 }, + "TRAILZ": { + "args": "I", + "doc": "TRAILZ(I) returns the number of trailing zero bits of an integer.", + "type": 3 + }, "TRANSFER": { "args": "SOURCE,MOLD,SIZE=size", "doc": "TRANSFER(SOURCE,MOLD,SIZE=size) interprets the bitwise representation of SOURCE in memory as if it is the representation of a variable or array of the same type and type parameters as MOLD.", @@ -737,19 +1297,44 @@ "doc": "TRIM(STRING) removes trailing blank characters of a string.", "type": 3 }, + "TTYNAM": { + "args": "UNIT", + "doc": "TTYNAM(UNIT) Get the name of a terminal device.", + "type": 3 + }, "UBOUND": { "args": "ARRAY,DIM=dim,KIND=kind", "doc": "UBOUND(ARRAY,DIM=dim,KIND=kind) returns the upper bounds of an array, or a single upper bound along the DIM dimension.", "type": 3 }, - "UPACK": { + "UCOBOUND": { + "args": "ARRAY,DIM=dim,KIND=kind", + "doc": "UCOBOUND(ARRAY,DIM=dim,KIND=kind) Returns the upper cobounds of a coarray, or a single upper cobound along the DIM codimension.", + "type": 3 + }, + "UMASK": { + "args": "MASK", + "doc": "UMASK(MASK) Sets the file creation mask to MASK.", + "type": 3 + }, + "UNLINK": { + "args": "PATH", + "doc": "UNLINK(PATH) Unlinks the file PATH.", + "type": 3 + }, + "UNPACK": { "args": "VECTOR,MASK,FIELD", - "doc": "UPACK(VECTOR,MASK,FIELD) stores the elements of VECTOR in an array of higher rank.", + "doc": "UNPACK(VECTOR,MASK,FIELD) Store the elements of VECTOR in an array of higher rank.", "type": 3 }, "VERIFY": { "args": "STRING,SET,BACK=back,KIND=kind", "doc": "VERIFY(STRING,SET,BACK=back,KIND=kind) verifies that all the characters in STRING belong to the set of characters in SET.", "type": 3 + }, + "XOR": { + "args": "I,J", + "doc": "XOR(I,J) Bitwise logical exclusive or.", + "type": 3 } } diff --git a/fortls/intrinsic_mods.json b/fortls/intrinsic_mods.json index aa3cfac9..ac57c03d 100644 --- a/fortls/intrinsic_mods.json +++ b/fortls/intrinsic_mods.json @@ -3,6 +3,330 @@ "type": 0, "name": "omp_lib", "children": [ + { + "name": "openmp_version", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_alloctrait", + "type": 4 + }, + { + "name": "omp_sched_static", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "omp_sched_dynamic", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "omp_sched_guided", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "omp_sched_auto", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "omp_proc_bind_false", + "type": 3, + "desc": "INTEGER(KIND=omp_proc_bind_kind)" + }, + { + "name": "omp_proc_bind_true", + "type": 3, + "desc": "INTEGER(KIND=omp_proc_bind_kind)" + }, + { + "name": "omp_proc_bind_master", + "type": 3, + "desc": "INTEGER(KIND=omp_proc_bind_kind)" + }, + { + "name": "omp_proc_bind_close", + "type": 3, + "desc": "INTEGER(KIND=omp_proc_bind_kind)" + }, + { + "name": "omp_proc_bind_spread", + "type": 3, + "desc": "INTEGER(KIND=omp_proc_bind_kind)" + }, + { + "name": "omp_lock_hint_none", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_lock_hint_uncontended", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_lock_hint_contended", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_lock_hint_nonspeculative", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_lock_hint_speculative", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_sync_hint_none", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_sync_hint_uncontended", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_sync_hint_contended", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_sync_hint_nonspeculative", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_sync_hint_speculative", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_hint_kind)" + }, + { + "name": "omp_pause_soft", + "type": 3, + "desc": "INTEGER(KIND=omp_pause_resource_kind)" + }, + { + "name": "omp_pause_hard", + "type": 3, + "desc": "INTEGER(KIND=omp_pause_resource_kind)" + }, + { + "name": "omp_atk_sync_hint", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_alignment", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_access", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_pool_size", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_fallback", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_fb_data", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_pinned", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atk_partition", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_key_kind)" + }, + { + "name": "omp_atv_default", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_false", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_true", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_contended", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_uncontended", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_serialized", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_sequential", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_private", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_all", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_thread", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_pteam", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_cgroup", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_default_mem_fb", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_null_fb", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_abort_fb", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_allocator_fb", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_environment", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_nearest", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_atv_blocked", + "type": 3, + "desc": "INTEGER(KIND=omp_alloctrait_val_kind)" + }, + { + "name": "omp_null_allocator", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_default_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_large_cap_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_const_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_high_bw_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_low_lat_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_cgroup_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_pteam_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_thread_mem_alloc", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_default_mem_space", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)" + }, + { + "name": "omp_large_cap_mem_space", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)" + }, + { + "name": "omp_const_mem_space", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)" + }, + { + "name": "omp_high_bw_mem_space", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)" + }, + { + "name": "omp_low_lat_mem_space", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)" + }, + { + "name": "omp_get_supported_active_levels", + "type": 2, + "return": "INTEGER" + }, { "name": "omp_get_num_threads", "type": 2, @@ -39,39 +363,178 @@ "return": "INTEGER" }, { - "name": "omp_get_active_level", - "type": 2, - "return": "INTEGER" - }, - { - "name": "omp_get_proc_bind", + "name": "omp_get_ancestor_thread_num", "type": 2, - "return": "INTEGER(KIND=omp_proc_bind_kind)" + "return": "INTEGER", + "args": "level", + "children": [ + { + "name": "level", + "type": 3, + "desc": "INTEGER" + } + ] }, { - "name": "omp_get_default_device", + "name": "omp_get_team_size", "type": 2, - "return": "INTEGER" + "return": "INTEGER", + "args": "level", + "children": [ + { + "name": "level", + "type": 3, + "desc": "INTEGER" + } + ] }, { - "name": "omp_get_num_devices", + "name": "omp_get_active_level", "type": 2, "return": "INTEGER" }, { - "name": "omp_get_num_teams", + "name": "omp_get_proc_bind", "type": 2, - "return": "INTEGER" + "return": "INTEGER(KIND=omp_proc_bind_kind)" }, { - "name": "omp_get_team_num", + "name": "omp_get_num_places", "type": 2, - "return": "INTEGER" + "return": "INTEGER(KIND=omp_proc_bind_kind)" }, { - "name": "omp_in_parallel", + "name": "omp_get_place_num_procs", "type": 2, - "return": "LOGICAL" + "return": "INTEGER", + "args": "place_num", + "children": [ + { + "name": "place_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_get_place_proc_ids", + "type": 1, + "args": "place_num,ids", + "children": [ + { + "name": "place_num", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "ids", + "type": 3, + "desc": "INTEGER", + "mods": ["DIMENSION(*)"] + } + ] + }, + { + "name": "omp_get_partition_place_nums", + "type": 1, + "args": "place_nums", + "children": [ + { + "name": "place_nums", + "type": 3, + "desc": "INTEGER", + "mods": ["DIMENSION(*)"] + } + ] + }, + { + "name": "omp_set_affinity_format", + "type": 1, + "args": "format", + "children": [ + { + "name": "format", + "type": 3, + "desc": "CHARACTER(LEN=*)", + "mods": ["INTENT(in)"] + } + ] + }, + { + "name": "omp_get_affinity_format", + "type": 2, + "return": "INTEGER", + "args": "buffer", + "children": [ + { + "name": "buffer", + "type": 3, + "desc": "CHARACTER(LEN=*)", + "mods": ["INTENT(out)"] + } + ] + }, + { + "name": "omp_display_affinity", + "type": 1, + "args": "format", + "children": [ + { + "name": "format", + "type": 3, + "desc": "CHARACTER(LEN=*)", + "mods": ["INTENT(in)"] + } + ] + }, + { + "name": "omp_capture_affinity", + "type": 1, + "args": "buffer,format", + "children": [ + { + "name": "buffer", + "type": 3, + "desc": "CHARACTER(LEN=*)", + "mods": ["INTENT(out)"] + }, + { + "name": "format", + "type": 3, + "desc": "CHARACTER(LEN=*)", + "mods": ["INTENT(in)"] + } + ] + }, + { + "name": "omp_get_default_device", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_get_num_devices", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_get_device_num", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_get_num_teams", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_get_team_num", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_in_parallel", + "type": 2, + "return": "LOGICAL" }, { "name": "omp_get_dynamic", @@ -84,316 +547,1516 @@ "return": "LOGICAL" }, { - "name": "omp_get_nested", - "type": 2, - "return": "LOGICAL" + "name": "omp_get_nested", + "type": 2, + "return": "LOGICAL" + }, + { + "name": "omp_in_final", + "type": 2, + "return": "LOGICAL" + }, + { + "name": "omp_is_initial_device", + "type": 2, + "return": "LOGICAL" + }, + { + "name": "omp_get_initial_device", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_get_max_task_priority", + "type": 2, + "return": "INTEGER" + }, + { + "name": "omp_pause_resource", + "type": 2, + "return": "INTEGER", + "args": "kind,device_num", + "children": [ + { + "name": "kind", + "type": 3, + "desc": "INTEGER(KIND=omp_pause_resource_kind)" + }, + { + "name": "device_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_pause_resource_all", + "type": 2, + "return": "INTEGER", + "args": "kind", + "children": [ + { + "name": "kind", + "type": 3, + "desc": "INTEGER(KIND=omp_pause_resource_kind)" + } + ] + }, + { + "name": "omp_get_wtime", + "type": 2, + "return": "DOUBLE PRECISION" + }, + { + "name": "omp_get_wtick", + "type": 2, + "return": "DOUBLE PRECISION" + }, + { + "name": "omp_fulfill_event", + "type": 1, + "args": "event", + "children": [ + { + "name": "event", + "type": 3, + "desc": "INTEGER(KIND=omp_event_handle_kind)" + } + ] + }, + { + "name": "omp_init_allocator", + "type": 2, + "return": "INTEGER(KIND=omp_allocator_handle_kind)", + "args": "memspace,ntraits,traits", + "children": [ + { + "name": "memspace", + "type": 3, + "desc": "INTEGER(KIND=omp_memspace_handle_kind)", + "mods": ["INTENT(in)"] + }, + { + "name": "ntraits", + "type": 3, + "desc": "INTEGER", + "mods": ["INTENT(in)"] + }, + { + "name": "traits", + "type": 3, + "desc": "TYPE(omp_alloctrait)", + "mods": ["DIMENSION(*)", "INTENT(in)"] + } + ] + }, + { + "name": "omp_destroy_allocator", + "type": 1, + "args": "allocator", + "children": [ + { + "name": "allocator", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)", + "mods": ["INTENT(in)"] + } + ] + }, + { + "name": "omp_set_default_allocator", + "type": 1, + "args": "allocator", + "children": [ + { + "name": "allocator", + "type": 3, + "desc": "INTEGER(KIND=omp_allocator_handle_kind)", + "mods": ["INTENT(in)"] + } + ] + }, + { + "name": "omp_get_default_allocator", + "type": 2, + "return": "INTEGER(KIND=omp_allocator_handle_kind)" + }, + { + "name": "omp_control_tool", + "type": 2, + "return": "INTEGER", + "args": "command,modifier", + "children": [ + { + "name": "command", + "type": 3, + "desc": "INTEGER(KIND=omp_control_tool_kind)" + }, + { + "name": "modifier", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_test_lock", + "type": 2, + "return": "LOGICAL", + "args": "svar", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + } + ] + }, + { + "name": "omp_test_nest_lock", + "type": 2, + "return": "LOGICAL", + "args": "nvar", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + } + ] + }, + { + "name": "omp_set_num_threads", + "type": 1, + "args": "num_threads", + "children": [ + { + "name": "num_threads", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_set_dynamic", + "type": 1, + "args": "dynamic_threads", + "children": [ + { + "name": "dynamic_threads", + "type": 3, + "desc": "LOGICAL" + } + ] + }, + { + "name": "omp_set_nested", + "type": 1, + "args": "nested", + "children": [ + { + "name": "nested", + "type": 3, + "desc": "LOGICAL" + } + ] + }, + { + "name": "omp_set_schedule", + "type": 1, + "args": "kind,chunk_size", + "children": [ + { + "name": "kind", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "chunk_size", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_get_schedule", + "type": 1, + "args": "kind,chunk_size", + "children": [ + { + "name": "kind", + "type": 3, + "desc": "INTEGER(KIND=omp_sched_kind)" + }, + { + "name": "chunk_size", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_set_max_active_levels", + "type": 1, + "args": "max_levels", + "children": [ + { + "name": "max_levels", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_set_default_device", + "type": 1, + "args": "device_num", + "children": [ + { + "name": "device_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "omp_init_lock", + "type": 1, + "args": "svar", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + } + ] + }, + { + "name": "omp_init_nest_lock", + "type": 1, + "args": "nvar", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + } + ] + }, + { + "name": "omp_init_lock_with_hint", + "type": 1, + "args": "svar,hint", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + }, + { + "name": "hint", + "type": 3, + "desc": "INTEGER(KIND=omp_sync_hint_kind)" + } + ] + }, + { + "name": "omp_init_nest_lock_with_hint", + "type": 1, + "args": "nvar,hint", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + }, + { + "name": "hint", + "type": 3, + "desc": "INTEGER(KIND=omp_sync_hint_kind)" + } + ] + }, + { + "name": "omp_destroy_lock", + "type": 1, + "args": "svar", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + } + ] + }, + { + "name": "omp_destroy_nest_lock", + "type": 1, + "args": "nvar", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + } + ] + }, + { + "name": "omp_set_lock", + "type": 1, + "args": "svar", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + } + ] + }, + { + "name": "omp_set_nest_lock", + "type": 1, + "args": "nvar", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + } + ] + }, + { + "name": "omp_unset_lock", + "type": 1, + "args": "svar", + "children": [ + { + "name": "svar", + "type": 3, + "desc": "INTEGER(KIND=omp_lock_kind)" + } + ] + }, + { + "name": "omp_unset_nest_lock", + "type": 1, + "args": "nvar", + "children": [ + { + "name": "nvar", + "type": 3, + "desc": "INTEGER(KIND=omp_nest_lock_kind)" + } + ] + } + ] + }, + "omp_lib_kinds": { + "type": 0, + "name": "omp_lib_kinds", + "children": [ + { + "name": "omp_allocator_handle_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_alloctrait_key_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_alloctrait_val_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_depend_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_lock_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_lock_hint_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_nest_lock_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_pause_resource_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_memspace_handle_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_proc_bind_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_sched_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "omp_sync_hint_kind", + "type": 3, + "desc": "INTEGER" + } + ] + }, + "openacc": { + "type": 0, + "name": "openacc", + "children": [ + { + "name": "openacc_version", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "acc_device_property_kind", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "acc_get_num_devices", + "type": 2, + "return": "INTEGER", + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_set_device_type", + "type": 1, + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_get_device_type", + "type": 2, + "return": "INTEGER(KIND=acc_device_kind)" + }, + { + "name": "acc_set_device_num", + "type": 1, + "args": "dev_num,dev_type", + "children": [ + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_get_device_num", + "type": 2, + "return": "INTEGER", + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_get_property", + "type": 2, + "return": "INTEGER(KIND=c_size_t)", + "args": "dev_num,dev_type,property", + "children": [ + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER", + "mods": ["VALUE"] + }, + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)", + "mods": ["VALUE"] + }, + { + "name": "property", + "type": 3, + "desc": "INTEGER(KIND=acc_device_property_kind)", + "mods": ["VALUE"] + } + ] + }, + { + "name": "acc_get_property_string", + "type": 1, + "args": "dev_num,dev_type,property,string", + "children": [ + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER", + "mods": ["VALUE"] + }, + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)", + "mods": ["VALUE"] + }, + { + "name": "property", + "type": 3, + "desc": "INTEGER(KIND=acc_device_property_kind)", + "mods": ["VALUE"] + }, + { + "name": "string", + "type": 3, + "desc": "CHARACTER(LEN=*)" + } + ] + }, + { + "name": "acc_init", + "type": 1, + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)", + "mods": ["VALUE"] + } + ] + }, + { + "name": "acc_shutdown", + "type": 1, + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_async_test", + "type": 2, + "return": "LOGICAL", + "args": "wait_arg", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_async_test_device", + "type": 2, + "return": "LOGICAL", + "args": "wait_arg,dev_num", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_async_test_all", + "type": 2, + "return": "LOGICAL" + }, + { + "name": "acc_async_test_all_device", + "type": 2, + "return": "LOGICAL", + "args": "dev_num", + "children": [ + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_wait", + "type": 1, + "args": "wait_arg", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_wait", + "type": 1, + "args": "wait_arg,dev_num", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_wait_async", + "type": 1, + "args": "wait_arg,async_arg", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_wait_device_async", + "type": 1, + "args": "wait_arg,async_arg,dev_num", + "children": [ + { + "name": "wait_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_wait_all", + "type": 1 + }, + { + "name": "acc_wait_all_device", + "type": 1, + "args": "dev_num", + "children": [ + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_wait_all_async", + "type": 1, + "args": "async_arg", + "children": [ + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_wait_all_device_async", + "type": 1, + "args": "async_arg,dev_num", + "children": [ + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + }, + { + "name": "dev_num", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_get_default_async", + "type": 2, + "return": "INTEGER(KIND=acc_device_kind)" + }, + { + "name": "acc_set_default_async", + "type": 1, + "args": "async_arg", + "children": [ + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_on_device", + "type": 2, + "return": "LOGICAL", + "args": "dev_type", + "children": [ + { + "name": "dev_type", + "type": 3, + "desc": "INTEGER(KIND=acc_device_kind)" + } + ] + }, + { + "name": "acc_copyin", + "type": 1, + "args": "data_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + } + ] + }, + { + "name": "acc_copyin", + "type": 1, + "args": "data_arg,bytes", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_copyin_async", + "type": 1, + "args": "data_arg,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_copyin_async", + "type": 1, + "args": "data_arg,bytes,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_create", + "type": 1, + "args": "data_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + } + ] + }, + { + "name": "acc_create", + "type": 1, + "args": "data_arg,bytes", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_create_async", + "type": 1, + "args": "data_arg,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_create_async", + "type": 1, + "args": "data_arg,bytes,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_copyout", + "type": 1, + "args": "data_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + } + ] + }, + { + "name": "acc_copyout", + "type": 1, + "args": "data_arg,bytes", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_copyout_async", + "type": 1, + "args": "data_arg,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_copyout_async", + "type": 1, + "args": "data_arg,bytes,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] + }, + { + "name": "acc_copyout_finalize", + "type": 1, + "args": "data_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + } + ] }, { - "name": "omp_in_final", - "type": 2, - "return": "LOGICAL" + "name": "acc_copyout_finalize", + "type": 1, + "args": "data_arg,bytes", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + } + ] }, { - "name": "omp_is_initial_device", - "type": 2, - "return": "LOGICAL" + "name": "acc_copyout_finalize_async", + "type": 1, + "args": "data_arg,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] }, { - "name": "omp_get_wtime", - "type": 2, - "return": "DOUBLE PRECISION" + "name": "acc_copyout_finalize_async", + "type": 1, + "args": "data_arg,bytes,async_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" + } + ] }, { - "name": "omp_get_wtick", - "type": 2, - "return": "DOUBLE PRECISION" + "name": "acc_delete", + "type": 1, + "args": "data_arg", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + } + ] }, { - "name": "omp_test_lock", - "type": 2, - "return": "LOGICAL", - "args": "svar", + "name": "acc_delete", + "type": 1, + "args": "data_arg,bytes", "children": [ { - "name": "svar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" } ] }, { - "name": "omp_test_nest_lock", - "type": 2, - "return": "LOGICAL", - "args": "nvar", + "name": "acc_delete_async", + "type": 1, + "args": "data_arg,async_arg", "children": [ { - "name": "nvar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_nest_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_set_num_threads", + "name": "acc_delete_async", "type": 1, - "args": "num_threads", + "args": "data_arg,bytes,async_arg", "children": [ { - "name": "num_threads", + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", "type": 3, "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_set_dynamic", + "name": "acc_delete_finalize", "type": 1, - "args": "dynamic_threads", + "args": "data_arg", "children": [ { - "name": "dynamic_threads", + "name": "data_arg", "type": 3, - "desc": "LOGICAL" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] } ] }, { - "name": "omp_set_nested", + "name": "acc_delete_finalize", "type": 1, - "args": "nested", + "args": "data_arg,bytes", "children": [ { - "name": "nested", + "name": "data_arg", "type": 3, - "desc": "LOGICAL" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" } ] }, { - "name": "omp_set_schedule", + "name": "acc_delete_finalize_async", "type": 1, - "args": "kind,modifier", + "args": "data_arg,async_arg", "children": [ { - "name": "kind", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] }, { - "name": "modifier", + "name": "async_arg", "type": 3, - "desc": "INTEGER" + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_get_schedule", + "name": "acc_delete_finalize_async", "type": 1, - "args": "kind,modifier", + "args": "data_arg,bytes,async_arg", "children": [ { - "name": "kind", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] }, { - "name": "modifier", + "name": "bytes", "type": 3, "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_set_max_active_levels", + "name": "acc_update_device", "type": 1, - "args": "max_levels", + "args": "data_arg", "children": [ { - "name": "max_levels", + "name": "data_arg", "type": 3, - "desc": "INTEGER" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] } ] }, { - "name": "omp_set_default_device", + "name": "acc_update_device", "type": 1, - "args": "device_num", + "args": "data_arg,bytes", "children": [ { - "name": "device_num", + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", "type": 3, "desc": "INTEGER" } ] }, { - "name": "omp_init_lock", + "name": "acc_update_device_async", "type": 1, - "args": "svar", + "args": "data_arg,async_arg", "children": [ { - "name": "svar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_init_nest_lock", + "name": "acc_update_device_async", "type": 1, - "args": "nvar", + "args": "data_arg,bytes,async_arg", "children": [ { - "name": "nvar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_nest_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_destroy_lock", + "name": "acc_update_self", "type": 1, - "args": "svar", + "args": "data_arg", "children": [ { - "name": "svar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] } ] }, { - "name": "omp_destroy_nest_lock", + "name": "acc_update_self", "type": 1, - "args": "nvar", + "args": "data_arg,bytes", "children": [ { - "name": "nvar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_nest_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" } ] }, { - "name": "omp_set_lock", + "name": "acc_update_self_async", "type": 1, - "args": "svar", + "args": "data_arg,async_arg", "children": [ { - "name": "svar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_set_nest_lock", + "name": "acc_update_self_async", "type": 1, - "args": "nvar", + "args": "data_arg,bytes,async_arg", "children": [ { - "name": "nvar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_nest_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "async_arg", + "type": 3, + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, { - "name": "omp_unset_lock", - "type": 1, - "args": "svar", + "name": "acc_is_present", + "type": 2, + "return": "LOGICAL", + "args": "data_arg", "children": [ { - "name": "svar", + "name": "data_arg", "type": 3, - "desc": "INTEGER(KIND=omp_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] } ] }, { - "name": "omp_unset_nest_lock", + "name": "acc_is_present", + "type": 2, + "return": "LOGICAL", + "args": "data_arg,bytes", + "children": [ + { + "name": "data_arg", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + } + ] + }, + { + "name": "acc_memcpy_d2d", "type": 1, - "args": "nvar", + "args": "data_arg_dest,data_arg_src,bytes,dev_num_dest,dev_num_src", "children": [ { - "name": "nvar", + "name": "data_arg_dest", "type": 3, - "desc": "INTEGER(KIND=omp_nest_lock_kind)" + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "data_arg_src", + "type": 3, + "desc": "TYPE(*)", + "mods": ["DIMENSION(*)"] + }, + { + "name": "bytes", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "dev_num_dest", + "type": 3, + "desc": "INTEGER" + }, + { + "name": "dev_num_src", + "type": 3, + "desc": "INTEGER" } ] } ] }, - "omp_lib_kinds": { + "openacc_kinds": { "type": 0, - "name": "omp_lib_kinds", + "name": "openacc_kinds", "children": [ { - "name": "omp_sched_kind", - "type": 3, - "desc": "INTEGER" - }, - { - "name": "omp_proc_bind_kind", - "type": 3, - "desc": "INTEGER" - }, - { - "name": "omp_lock_kind", - "type": 3, - "desc": "INTEGER" - }, - { - "name": "omp_nest_lock_kind", + "name": "acc_device_kind", "type": 3, "desc": "INTEGER" }, { - "name": "omp_sched_static", - "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" - }, - { - "name": "omp_sched_dynamic", + "name": "acc_device_none", "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" + "desc": "INTEGER(KIND=acc_device_kind)" }, { - "name": "omp_sched_guided", + "name": "acc_device_default", "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" + "desc": "INTEGER(KIND=acc_device_kind)" }, { - "name": "omp_sched_auto", + "name": "acc_device_host", "type": 3, - "desc": "INTEGER(KIND=omp_sched_kind)" + "desc": "INTEGER(KIND=acc_device_kind)" }, { - "name": "omp_proc_bind_false", + "name": "acc_device_not_host", "type": 3, - "desc": "INTEGER(KIND=omp_proc_bind_kind)" + "desc": "INTEGER(KIND=acc_device_kind)" }, { - "name": "omp_proc_bind_true", + "name": "acc_device_nvidia", "type": 3, - "desc": "INTEGER(KIND=omp_proc_bind_kind)" + "desc": "INTEGER(KIND=acc_device_kind)" }, { - "name": "omp_proc_bind_master", + "name": "acc_handle_kind", "type": 3, - "desc": "INTEGER(KIND=omp_proc_bind_kind)" + "desc": "INTEGER" }, { - "name": "omp_proc_bind_close", + "name": "acc_async_noval", "type": 3, - "desc": "INTEGER(KIND=omp_proc_bind_kind)" + "desc": "INTEGER(KIND=acc_handle_kind)" }, { - "name": "omp_proc_bind_spread", + "name": "acc_async_sync", "type": 3, - "desc": "INTEGER(KIND=omp_proc_bind_kind)" + "desc": "INTEGER(KIND=acc_handle_kind)" } ] }, @@ -804,7 +2467,7 @@ "name": "c_ptr_2", "type": 3, "desc": "TYPE(c_ptr)", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -823,13 +2486,13 @@ "name": "fptr", "type": 3, "desc": "ANY", - "mods": ["POINTER","INTENT(out)"] + "mods": ["POINTER", "INTENT(out)"] }, { "name": "shape", "type": 3, "desc": "INTEGER", - "mods": ["DIMENSION(:)","INTENT(in)"] + "mods": ["DIMENSION(:)", "INTENT(in)"] } ] }, @@ -848,7 +2511,7 @@ "name": "fptr", "type": 3, "desc": "PROCEDURE", - "mods": ["POINTER","INTENT(out)"] + "mods": ["POINTER", "INTENT(out)"] } ] }, @@ -876,7 +2539,7 @@ "name": "x", "type": 3, "desc": "ANY", - "mods": ["POINTER","INTENT(in)"] + "mods": ["POINTER", "INTENT(in)"] } ] }, @@ -941,7 +2604,6 @@ "desc": "TYPE(ieee_flag_type)", "mods": ["DIMENSION(5)"] } - ] }, "ieee_arithmetic": { @@ -1351,13 +3013,13 @@ "name": "P", "type": 3, "desc": "INTEGER", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] }, { "name": "R", "type": 3, "desc": "INTEGER", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1371,7 +3033,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1385,7 +3047,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1399,7 +3061,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1419,7 +3081,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1447,7 +3109,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1461,7 +3123,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1475,7 +3137,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1495,7 +3157,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1509,7 +3171,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, @@ -1523,7 +3185,7 @@ "name": "X", "type": 3, "desc": "REAL", - "mods": ["OPTIONAL","INTENT(in)"] + "mods": ["OPTIONAL", "INTENT(in)"] } ] }, diff --git a/fortls/intrinsics.py b/fortls/intrinsics.py index cde79420..d3f58c46 100644 --- a/fortls/intrinsics.py +++ b/fortls/intrinsics.py @@ -181,6 +181,7 @@ def add_children(json_obj, fort_obj): int_funs.append(create_int_object(name, json_obj, json_obj["type"])) # Definitions taken from gfortran documentation # (https://gcc.gnu.org/onlinedocs/gfortran/Intrinsic-Modules.html#Intrinsic-Modules) + # Update OpenACC from here https://www.openacc.org/specification json_file = os.path.join( os.path.dirname(os.path.abspath(__file__)), "intrinsic_mods.json" ) diff --git a/fortls/keywords.json b/fortls/keywords.json index 99c3f5f1..81f611d0 100644 --- a/fortls/keywords.json +++ b/fortls/keywords.json @@ -52,10 +52,10 @@ } }, "vis": { - "PRIVATE": { }, - "PUBLIC": { } + "PRIVATE": {}, + "PUBLIC": {} }, "param": { - "PARAMETER": { } + "PARAMETER": {} } } diff --git a/fortls/langserver.py b/fortls/langserver.py index 3f395cfe..99c8813a 100644 --- a/fortls/langserver.py +++ b/fortls/langserver.py @@ -1,4 +1,3 @@ -# TODO: make FORTRAN_EXT_REGEX be able to update from user input extensions # TODO: enable jsonc C-style comments from __future__ import annotations @@ -9,6 +8,7 @@ import traceback from multiprocessing import Pool from pathlib import Path +from typing import Pattern # Local modules from fortls.constants import ( @@ -52,11 +52,11 @@ LOGICAL_REGEX, NUMBER_REGEX, SQ_STRING_REGEX, + src_file_exts, ) log = logging.getLogger(__name__) # Global regexes -FORTRAN_EXT_REGEX = re.compile(r"\.F(77|90|95|03|08|OR|PP)?$", re.I) # TODO: I think this can be replaced by fortls.regex_patterns type & class TYPE_DEF_REGEX = re.compile(r"[ ]*(TYPE|CLASS)[ ]*\([a-z0-9_ ]*$", re.I) # TODO: I think this can be replaced by fortls.regex_patterns @@ -70,14 +70,11 @@ def init_file(filepath, pp_defs, pp_suffixes, include_dirs): # file_obj = fortran_file(filepath, pp_suffixes) - err_str = file_obj.load_from_disk() - if err_str is not None: + err_str, _ = file_obj.load_from_disk() + if err_str: return None, err_str - # try: - file_ast = process_file( - file_obj, True, pp_defs=pp_defs, include_dirs=include_dirs - ) + file_ast = process_file(file_obj, pp_defs=pp_defs, include_dirs=include_dirs) except: log.error("Error while parsing file %s", filepath, exc_info=True) return None, "Error during parsing" @@ -86,7 +83,7 @@ def init_file(filepath, pp_defs, pp_suffixes, include_dirs): class LangServer: - def __init__(self, conn, debug_log=False, settings={}): + def __init__(self, conn, debug_log: bool = False, settings: dict = {}): self.conn = conn self.running = True self.root_path = None @@ -97,13 +94,15 @@ def __init__(self, conn, debug_log=False, settings={}): self.link_version = 0 self.source_dirs = set() self.excl_paths = set() - self.excl_suffixes = [] + self.incl_suffixes: list[str] = [] + self.excl_suffixes: list[str] = [] self.post_messages = [] self.pp_suffixes = None self.pp_defs = {} self.include_dirs = [] self.streaming = True self.debug_log = debug_log + self.FORTRAN_SRC_EXT_REGEX: Pattern[str] = src_file_exts() # Intrinsic (re-loaded during initialize) ( self.statements, @@ -222,7 +221,9 @@ def serve_initialize(self, request): ) self.source_dirs.add(self.root_path) self.__config_logger(request) - self.__load_config_file() + init_debug_log = self.__load_config_file() + if init_debug_log: + self.__config_logger(request) self.__load_intrinsics() self.__add_source_dirs() @@ -685,7 +686,13 @@ def build_comp( ) return item_list - def get_definition(self, def_file, def_line, def_char, hover_req=False): + def get_definition( + self, + def_file: fortran_file, + def_line: int, + def_char: int, + hover_req: bool = False, + ): # Get full line (and possible continuations) from file pre_lines, curr_line, _ = def_file.get_code_line( def_line, forward=False, strip_comment=True @@ -701,9 +708,18 @@ def get_definition(self, def_file, def_line, def_char, hover_req=False): def_name = expand_name(curr_line, def_char) except: return None - # print(var_stack, def_name) if def_name == "": return None + # Search in Preprocessor defined variables + if def_name in def_file.pp_defs: + var = fortran_var( + def_file.ast, + def_line + 1, + def_name, + f"#define {def_name} {def_file.pp_defs.get(def_name)}", + [], + ) + return var curr_scope = def_file.ast.get_inner_scope(def_line + 1) # Traverse type tree if necessary if is_member: @@ -995,7 +1011,7 @@ def serve_definition(self, request): } return None - def serve_hover(self, request): + def serve_hover(self, request: dict): def create_hover(string, highlight): if highlight: return {"language": self.hover_language, "value": string} @@ -1023,12 +1039,12 @@ def create_signature_hover(): pass # Get parameters from request - params = request["params"] - uri = params["textDocument"]["uri"] - def_line = params["position"]["line"] - def_char = params["position"]["character"] + params: dict = request["params"] + uri: str = params["textDocument"]["uri"] + def_line: int = params["position"]["line"] + def_char: int = params["position"]["character"] path = path_from_uri(uri) - file_obj = self.workspace.get(path) + file_obj: fortran_file = self.workspace.get(path) if file_obj is None: return None # Find object @@ -1281,6 +1297,7 @@ def serve_onChange(self, request): # tmp_file.ast.resolve_links(self.obj_tree, self.link_version) elif file_obj.preproc: file_obj.preprocess(pp_defs=self.pp_defs) + self.pp_defs = {**self.pp_defs, **file_obj.pp_defs} def serve_onOpen(self, request): self.serve_onSave(request, did_open=True) @@ -1339,16 +1356,18 @@ def update_workspace_file( return False, None else: return False, "File does not exist" # Error during load - hash_old = file_obj.hash - err_string = file_obj.load_from_disk() - if err_string is not None: - log.error(err_string + ": %s", filepath) + err_string, file_changed = file_obj.load_from_disk() + if err_string: + log.error(f"{err_string} : {filepath}") return False, err_string # Error during file read - if hash_old == file_obj.hash: + if not file_changed: return False, None ast_new = process_file( - file_obj, True, pp_defs=self.pp_defs, include_dirs=self.include_dirs + file_obj, pp_defs=self.pp_defs, include_dirs=self.include_dirs ) + # Add the included read in pp_defs from to the ones specified in the + # configuration file + self.pp_defs = {**self.pp_defs, **file_obj.pp_defs} except: log.error("Error while parsing file %s", filepath, exc_info=True) return False, "Error during parsing" # Error during parsing @@ -1415,7 +1434,7 @@ def serve_default(self, request): code=-32601, message="method {} not found".format(request["method"]) ) - def __load_config_file(self) -> None: + def __load_config_file(self) -> bool | None: """Loads the configuration file for the Language Server""" # Check for config file @@ -1442,7 +1461,13 @@ def __load_config_file(self) -> None: self.__load_config_file_preproc(config_dict) # Debug options - self.debug_log = config_dict.get("debug_log", self.debug_log) + debugging: bool = config_dict.get("debug_log", self.debug_log) + # If conf option is different than the debug option passed as a + # command line argument return True so that debug log is setup + if debugging != self.debug_log and not self.debug_log: + self.debug_log = True + return True + return False except FileNotFoundError: msg = f"Error settings file '{self.config}' not found" @@ -1476,6 +1501,9 @@ def __load_config_file_dirs(self, config_dict) -> None: self.source_dirs = {i for i in self.source_dirs if i not in self.excl_paths} def __load_config_file_general(self, config_dict) -> None: + self.incl_suffixes = config_dict.get("incl_suffixes", []) + # Update the source file REGEX + self.FORTRAN_SRC_EXT_REGEX = src_file_exts(self.incl_suffixes) self.excl_suffixes = config_dict.get("excl_suffixes", []) self.lowercase_intrinsics = config_dict.get( "lowercase_intrinsics", self.lowercase_intrinsics @@ -1520,7 +1548,7 @@ def __add_source_dirs(self) -> None: self.source_dirs = set() for root, dirs, files in os.walk(self.root_path): # Match not found - if not list(filter(FORTRAN_EXT_REGEX.search, files)): + if not list(filter(self.FORTRAN_SRC_EXT_REGEX.search, files)): continue if root not in self.source_dirs and root not in self.excl_paths: self.source_dirs.add(str(Path(root).resolve())) @@ -1548,7 +1576,7 @@ def __get_source_files(self) -> list[str]: if not os.path.isfile(p): continue # File extension must match supported extensions - if not FORTRAN_EXT_REGEX.search(f): + if not self.FORTRAN_SRC_EXT_REGEX.search(f): continue # File cannot be in excluded paths/files if p in self.excl_paths: diff --git a/fortls/objects.py b/fortls/objects.py index 3466bc2e..30b74f0d 100644 --- a/fortls/objects.py +++ b/fortls/objects.py @@ -1,7 +1,10 @@ +from __future__ import annotations, print_function + import copy import os import re -from collections import namedtuple +from typing import Dict, NamedTuple, List, Pattern +from dataclasses import dataclass from fortls.constants import ( ASSOC_TYPE_ID, @@ -27,10 +30,59 @@ from fortls.regex_patterns import CLASS_VAR_REGEX, DEF_KIND_REGEX # Helper types -USE_info = namedtuple("USE_info", ["only_list", "rename_map"]) - - -def get_use_tree(scope, use_dict, obj_tree, only_list=[], rename_map={}, curr_path=[]): +VAR_info = NamedTuple( + "VAR_info", [("type_word", str), ("keywords", List[str]), ("var_names", List[str])] +) +SUB_info = NamedTuple( + "SUB_info", + [("name", str), ("args", str), ("mod_flag", bool), ("keywords", List[str])], +) +FUN_info = NamedTuple( + "FUN_info", + [ + ("name", str), + ("args", str), + ("return_type", None), + ("return_var", str), + ("mod_flag", bool), + ("keywords", List[str]), + ], +) +SELECT_info = NamedTuple( + "SELECT_info", [("type", int), ("binding", str), ("desc", str)] +) +CLASS_info = NamedTuple( + "CLASS_info", [("name", str), ("parent", str), ("keywords", List[str])] +) +USE_info = NamedTuple( + "USE_info", + [("mod_name", str), ("only_list", List[str]), ("rename_map", Dict[str, str])], +) +GEN_info = NamedTuple( + "GEN_info", + [("bound_name", str), ("pro_links", List[str]), ("vis_flag", int)], +) +SMOD_info = NamedTuple("SMOD_info", [("name", str), ("parent", str)]) +INT_info = NamedTuple("INT_info", [("name", str), ("abstract", bool)]) +VIS_info = NamedTuple("VIS_info", [("type", int), ("obj_names", List[str])]) + + +@dataclass +class INCLUDE_info: + line_number: str + path: str + file: None # fortran_file + scope_objs: list[str] + + +def get_use_tree( + scope: fortran_scope, + use_dict: dict, + obj_tree: dict, + only_list: list = [], + rename_map: dict = {}, + curr_path: list = [], +): def intersect_only(use_stmnt): tmp_list = [] tmp_map = rename_map.copy() @@ -80,12 +132,14 @@ def intersect_only(use_stmnt): rename_map=merged_rename ) else: - use_dict[use_stmnt.mod_name] = USE_info(set(), {}) + use_dict[use_stmnt.mod_name] = USE_info(use_stmnt.mod_name, set(), {}) # Skip if we have already visited module with the same only list if old_len == len(use_dict_mod.only_list): continue else: - use_dict[use_stmnt.mod_name] = USE_info(set(merged_use_list), merged_rename) + use_dict[use_stmnt.mod_name] = USE_info( + use_stmnt.mod_name, set(merged_use_list), merged_rename + ) # Descend USE tree use_dict = get_use_tree( obj_tree[use_stmnt.mod_name][0], @@ -98,8 +152,16 @@ def intersect_only(use_stmnt): return use_dict -def find_in_scope(scope, var_name, obj_tree, interface=False, local_only=False): - def check_scope(local_scope, var_name_lower, filter_public=False): +def find_in_scope( + scope: fortran_scope, + var_name: str, + obj_tree: dict, + interface: bool = False, + local_only: bool = False, +): + def check_scope( + local_scope: fortran_scope, var_name_lower: str, filter_public: bool = False + ): for child in local_scope.get_children(): if child.name.startswith("#GEN_INT"): tmp_var = check_scope(child, var_name_lower, filter_public) @@ -120,6 +182,14 @@ def check_scope(local_scope, var_name_lower, filter_public=False): tmp_var = check_scope(scope, var_name_lower) if local_only or (tmp_var is not None): return tmp_var + # Check INCLUDE statements + if scope.file_ast.include_statements: + strip_str = var_name.replace('"', "") + strip_str = strip_str.replace("'", "") + for inc in scope.file_ast.include_statements: + if strip_str == inc.path: + return fortran_include(inc.file.ast, inc.line_number, inc.path) + # Setup USE search use_dict = get_use_tree(scope, {}, obj_tree) # Look in found use modules @@ -159,8 +229,10 @@ def check_scope(local_scope, var_name_lower, filter_public=False): return None -def find_in_workspace(obj_tree, query, filter_public=False, exact_match=False): - def add_children(mod_obj, query): +def find_in_workspace( + obj_tree: dict, query: str, filter_public: bool = False, exact_match: bool = False +): + def add_children(mod_obj, query: str): tmp_list = [] for child_obj in mod_obj.get_children(filter_public): if child_obj.name.lower().find(query) >= 0: @@ -187,7 +259,7 @@ def add_children(mod_obj, query): return matching_symbols -def climb_type_tree(var_stack, curr_scope, obj_tree): +def climb_type_tree(var_stack, curr_scope: fortran_scope, obj_tree: dict): """Walk up user-defined type sequence to determine final field type""" # Find base variable in current scope iVar = 0 @@ -219,27 +291,35 @@ def climb_type_tree(var_stack, curr_scope, obj_tree): # Helper classes class USE_line: - def __init__(self, mod_name, line_number, only_list=[], rename_map={}): - self.mod_name = mod_name.lower() - self.line_number = line_number - self.only_list = [only.lower() for only in only_list] - self.rename_map = { + def __init__( + self, + mod_name: str, + line_number: int, + only_list: list = [], + rename_map: dict = {}, + ): + self.mod_name: str = mod_name.lower() + self.line_number: int = line_number + self.only_list: list = [only.lower() for only in only_list] + self.rename_map: dict = { key.lower(): value.lower() for key, value in rename_map.items() } class fortran_diagnostic: - def __init__(self, sline, message, severity=1, find_word=None): - self.sline = sline - self.message = message - self.severity = severity - self.find_word = find_word - self.has_related = False + def __init__( + self, sline: int, message: str, severity: int = 1, find_word: str = None + ): + self.sline: int = sline + self.message: str = message + self.severity: int = severity + self.find_word: str = find_word + self.has_related: bool = False self.related_path = None self.related_line = None self.related_message = None - def add_related(self, path, line, message): + def add_related(self, path: str, line: int, message: str): self.has_related = True self.related_path = path self.related_line = line @@ -281,29 +361,29 @@ def build(self, file_obj): # Fortran object classes class fortran_obj: def __init__(self): - self.vis = 0 - self.def_vis = 0 - self.doc_str = None + self.vis: int = 0 + self.def_vis: int = 0 + self.doc_str: str = None self.parent = None - self.eline = -1 + self.eline: int = -1 self.implicit_vars = None - def set_default_vis(self, new_vis): + def set_default_vis(self, new_vis: int): self.def_vis = new_vis - def set_visibility(self, new_vis): + def set_visibility(self, new_vis: int): self.vis = new_vis def set_parent(self, parent_obj): self.parent = parent_obj - def add_doc(self, doc_str): + def add_doc(self, doc_str: str): self.doc_str = doc_str def update_fqsn(self, enc_scope=None): return None - def end(self, line_number): + def end(self, line_number: int): self.eline = line_number def resolve_inherit(self, obj_tree, inherit_version): @@ -389,30 +469,29 @@ def check_definition(self, obj_tree, known_types={}, interface=False): class fortran_scope(fortran_obj): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) - def base_setup(self, file_ast, sline, name, keywords=[]): + def base_setup(self, file_ast, sline: int, name: str, keywords: list = []): self.file_ast = file_ast - self.sline = sline - self.eline = sline - self.name = name - self.children = [] - self.members = [] - self.use = [] - self.keywords = keywords + self.sline: int = sline + self.eline: int = sline + self.name: str = name + self.children: list = [] + self.members: list = [] + self.use: list[USE_line] = [] + self.keywords: list = keywords self.inherit = None self.parent = None - self.vis = 0 - self.def_vis = 0 + self.vis: int = 0 + self.def_vis: int = 0 self.contains_start = None - self.doc_str = None + self.doc_str: str = None self.implicit_vars = None self.implicit_line = None + self.FQSN: str = self.name.lower() if file_ast.enc_scope_name is not None: self.FQSN = file_ast.enc_scope_name.lower() + "::" + self.name.lower() - else: - self.FQSN = self.name.lower() def copy_from(self, copy_source): self.file_ast = copy_source.file_ast @@ -664,13 +743,20 @@ def check_valid_parent(self): return True +class fortran_include(fortran_scope): + def get_dec(self): + return "INCLUDE" + + class fortran_program(fortran_module): def get_desc(self): return "PROGRAM" class fortran_submodule(fortran_module): - def __init__(self, file_ast, line_number, name, ancestor_name=None): + def __init__( + self, file_ast, line_number: int, name: str, ancestor_name: str = None + ): self.base_setup(file_ast, line_number, name) self.ancestor_name = ancestor_name self.ancestor_obj = None @@ -745,15 +831,21 @@ def require_link(self): class fortran_subroutine(fortran_scope): def __init__( - self, file_ast, line_number, name, args="", mod_flag=False, keywords=[] + self, + file_ast, + line_number: int, + name: str, + args: str = "", + mod_flag: bool = False, + keywords: list = [], ): self.base_setup(file_ast, line_number, name, keywords=keywords) - self.args = args.replace(" ", "") - self.args_snip = self.args - self.arg_objs = [] - self.in_children = [] - self.missing_args = [] - self.mod_scope = mod_flag + self.args: str = args.replace(" ", "") + self.args_snip: str = self.args + self.arg_objs: list = [] + self.in_children: list = [] + self.missing_args: list = [] + self.mod_scope: bool = mod_flag def is_mod_scope(self): return self.mod_scope @@ -955,27 +1047,26 @@ class fortran_function(fortran_subroutine): def __init__( self, file_ast, - line_number, - name, - args="", - mod_flag=False, - keywords=[], + line_number: int, + name: str, + args: str = "", + mod_flag: bool = False, + keywords: list = [], return_type=None, result_var=None, ): self.base_setup(file_ast, line_number, name, keywords=keywords) - self.args = args.replace(" ", "").lower() - self.args_snip = self.args - self.arg_objs = [] - self.in_children = [] - self.missing_args = [] - self.mod_scope = mod_flag + self.args: str = args.replace(" ", "").lower() + self.args_snip: str = self.args + self.arg_objs: list = [] + self.in_children: list = [] + self.missing_args: list = [] + self.mod_scope: bool = mod_flag self.result_var = result_var self.result_obj = None + self.return_type = None if return_type is not None: self.return_type = return_type[0] - else: - self.return_type = None def copy_interface(self, copy_source): # Copy arguments and returns @@ -1077,10 +1168,10 @@ def get_interface(self, name_replace=None, change_arg=-1, change_strings=None): class fortran_type(fortran_scope): - def __init__(self, file_ast, line_number, name, keywords): + def __init__(self, file_ast, line_number: int, name: str, keywords: list): self.base_setup(file_ast, line_number, name, keywords=keywords) # - self.in_children = [] + self.in_children: list = [] self.inherit = None self.inherit_var = None self.inherit_tmp = None @@ -1242,7 +1333,7 @@ def get_actions(self, sline, eline): class fortran_block(fortran_scope): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) def get_type(self, no_link=False): @@ -1259,7 +1350,7 @@ def req_named_end(self): class fortran_do(fortran_block): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) def get_type(self, no_link=False): @@ -1270,7 +1361,7 @@ def get_desc(self): class fortran_where(fortran_block): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) def get_type(self, no_link=False): @@ -1281,7 +1372,7 @@ def get_desc(self): class fortran_if(fortran_block): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) def get_type(self, no_link=False): @@ -1292,7 +1383,7 @@ def get_desc(self): class fortran_associate(fortran_block): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) self.assoc_links = [] @@ -1327,7 +1418,7 @@ def require_link(self): class fortran_enum(fortran_block): - def __init__(self, file_ast, line_number, name): + def __init__(self, file_ast, line_number: int, name: str): self.base_setup(file_ast, line_number, name) def get_type(self, no_link=False): @@ -1338,7 +1429,7 @@ def get_desc(self): class fortran_select(fortran_block): - def __init__(self, file_ast, line_number, name, select_info): + def __init__(self, file_ast, line_number: int, name: str, select_info): self.base_setup(file_ast, line_number, name) self.select_type = select_info.type self.binding_name = None @@ -1395,7 +1486,7 @@ def create_binding_variable(self, file_ast, line_number, var_desc, case_type): class fortran_int(fortran_scope): - def __init__(self, file_ast, line_number, name, abstract=False): + def __init__(self, file_ast, line_number: list, name: str, abstract: bool = False): self.base_setup(file_ast, line_number, name) self.mems = [] self.abstract = abstract @@ -1433,11 +1524,11 @@ class fortran_var(fortran_obj): def __init__( self, file_ast, - line_number, - name, - var_desc, - keywords, - keyword_info={}, + line_number: int, + name: str, + var_desc: str, + keywords: list, + keyword_info: dict = {}, link_obj=None, ): self.base_setup( @@ -1445,33 +1536,38 @@ def __init__( ) def base_setup( - self, file_ast, line_number, name, var_desc, keywords, keyword_info, link_obj + self, + file_ast, + line_number: int, + name: str, + var_desc: str, + keywords: list, + keyword_info: dict, + link_obj: str, ): self.file_ast = file_ast - self.sline = line_number - self.eline = line_number - self.name = name - self.desc = var_desc - self.keywords = keywords - self.keyword_info = keyword_info - self.doc_str = None - self.callable = CLASS_VAR_REGEX.match(var_desc) is not None - self.children = [] - self.use = [] - self.vis = 0 + self.sline: int = line_number + self.eline: int = line_number + self.name: int = name + self.desc: str = var_desc + self.keywords: list = keywords + self.keyword_info: dict = keyword_info + self.doc_str: str = None + self.callable: bool = CLASS_VAR_REGEX.match(var_desc) is not None + self.children: list = [] + self.use: list[USE_line] = [] + self.vis: int = 0 self.parent = None self.link_obj = None self.type_obj = None - self.is_const = False + self.is_const: bool = False self.param_val: str = None + self.link_name: str = None + self.FQSN: str = self.name.lower() if link_obj is not None: self.link_name = link_obj.lower() - else: - self.link_name = None if file_ast.enc_scope_name is not None: self.FQSN = file_ast.enc_scope_name.lower() + "::" + self.name.lower() - else: - self.FQSN = self.name.lower() if self.keywords.count(KEYWORD_ID_DICT["public"]) > 0: self.vis = 1 if self.keywords.count(KEYWORD_ID_DICT["private"]) > 0: @@ -1631,18 +1727,18 @@ class fortran_meth(fortran_var): def __init__( self, file_ast, - line_number, - name, - var_desc, - keywords, - keyword_info, + line_number: int, + name: str, + var_desc: str, + keywords: list, + keyword_info: dict, link_obj=None, ): self.base_setup( file_ast, line_number, name, var_desc, keywords, keyword_info, link_obj ) - self.drop_arg = -1 - self.pass_name = keyword_info.get("pass") + self.drop_arg: int = -1 + self.pass_name: str = keyword_info.get("pass") if link_obj is None: self.link_name = get_paren_substring(var_desc.lower()) @@ -1759,27 +1855,27 @@ def check_definition(self, obj_tree, known_types={}, interface=False): class fortran_ast: def __init__(self, file_obj=None): self.file = file_obj - self.path = None + self.path: str = None if file_obj is not None: self.path = file_obj.path - self.global_dict = {} - self.scope_list = [] - self.variable_list = [] - self.public_list = [] - self.private_list = [] - self.scope_stack = [] - self.end_stack = [] - self.pp_if = [] - self.include_stmnts = [] - self.end_errors = [] - self.parse_errors = [] - self.inherit_objs = [] - self.linkable_objs = [] + self.global_dict: dict = {} + self.scope_list: list = [] + self.variable_list: list = [] + self.public_list: list = [] + self.private_list: list = [] + self.scope_stack: list = [] + self.end_stack: list = [] + self.pp_if: list = [] + self.include_statements: list = [] + self.end_errors: list = [] + self.parse_errors: list = [] + self.inherit_objs: list = [] + self.linkable_objs: list = [] self.none_scope = None self.inc_scope = None self.current_scope = None - self.END_SCOPE_REGEX = None - self.enc_scope_name = None + self.END_SCOPE_REGEX: Pattern = None + self.enc_scope_name: str = None self.last_obj = None self.pending_doc = None @@ -1799,7 +1895,11 @@ def get_enc_scope_name(self): return self.current_scope.FQSN def add_scope( - self, new_scope, END_SCOPE_REGEX, exportable=True, req_container=False + self, + new_scope: fortran_scope, + END_SCOPE_REGEX, + exportable: bool = True, + req_container: bool = False, ): self.scope_list.append(new_scope) if new_scope.require_inherit(): @@ -1828,7 +1928,7 @@ def add_scope( self.last_obj.add_doc(self.pending_doc) self.pending_doc = None - def end_scope(self, line_number, check=True): + def end_scope(self, line_number: int, check: bool = True): if ( (self.current_scope is None) or (self.current_scope is self.none_scope) ) and check: @@ -1861,21 +1961,27 @@ def add_variable(self, new_var): def add_int_member(self, key): self.current_scope.add_member(key) - def add_private(self, name): + def add_private(self, name: str): self.private_list.append(self.enc_scope_name + "::" + name) - def add_public(self, name): + def add_public(self, name: str): self.public_list.append(self.enc_scope_name + "::" + name) - def add_use(self, mod_word, line_number, only_list=[], rename_map={}): + def add_use( + self, + mod_word: str, + line_number: int, + only_list: list = [], + rename_map: dict = {}, + ): if self.current_scope is None: self.create_none_scope() self.current_scope.add_use(mod_word, line_number, only_list, rename_map) - def add_include(self, path, line_number): - self.include_stmnts.append([line_number, path, []]) + def add_include(self, path: str, line_number: int): + self.include_statements.append(INCLUDE_info(line_number, path, None, [])) - def add_doc(self, doc_string, forward=False): + def add_doc(self, doc_string: str, forward: bool = False): if doc_string == "": return if forward: @@ -1884,14 +1990,14 @@ def add_doc(self, doc_string, forward=False): if self.last_obj is not None: self.last_obj.add_doc(doc_string) - def start_ppif(self, line_number): + def start_ppif(self, line_number: int): self.pp_if.append([line_number - 1, -1]) def end_ppif(self, line_number): if len(self.pp_if) > 0: self.pp_if[-1][1] = line_number - 1 - def get_scopes(self, line_number=None): + def get_scopes(self, line_number: int = None): if line_number is None: return self.scope_list scope_list = [] @@ -1904,7 +2010,7 @@ def get_scopes(self, line_number=None): return [self.none_scope] return scope_list - def get_inner_scope(self, line_number): + def get_inner_scope(self, line_number: int): scope_sline = -1 curr_scope = None for scope in self.scope_list: @@ -1916,7 +2022,7 @@ def get_inner_scope(self, line_number): return self.none_scope return curr_scope - def get_object(self, FQSN): + def get_object(self, FQSN: str): FQSN_split = FQSN.split("::") curr_obj = self.global_dict.get(FQSN_split[0]) if curr_obj is None: @@ -1946,19 +2052,19 @@ def get_object(self, FQSN): curr_obj = next_obj return curr_obj - def resolve_includes(self, workspace, path=None): + def resolve_includes(self, workspace, path: str = None): file_dir = os.path.dirname(self.path) - for include_path in self.include_stmnts: - file_path = os.path.normpath(os.path.join(file_dir, include_path[1])) - if path is not None: - if not (path == file_path): - continue - parent_scope = self.get_inner_scope(include_path[0]) - added_entities = include_path[2] + for inc in self.include_statements: + file_path = os.path.normpath(os.path.join(file_dir, inc.path)) + if path and not (path == file_path): + continue + parent_scope = self.get_inner_scope(inc.line_number) + added_entities = inc.scope_objs if file_path in workspace: include_file = workspace[file_path] include_ast = include_file.ast - if include_ast.none_scope is not None: + inc.file = include_file + if include_ast.none_scope: if include_ast.inc_scope is None: include_ast.inc_scope = include_ast.none_scope # Remove old objects @@ -1970,7 +2076,7 @@ def resolve_includes(self, workspace, path=None): parent_scope.add_child(child) child.update_fqsn(parent_scope.FQSN) include_ast.none_scope = parent_scope - include_path[2] = added_entities + inc.scope_objs = added_entities def resolve_links(self, obj_tree, link_version): for inherit_obj in self.inherit_objs: @@ -1978,7 +2084,7 @@ def resolve_links(self, obj_tree, link_version): for linkable_obj in self.linkable_objs: linkable_obj.resolve_link(obj_tree) - def close_file(self, line_number): + def close_file(self, line_number: int): # Close open scopes while self.current_scope is not None: self.end_scope(line_number, check=False) diff --git a/fortls/parse_fortran.py b/fortls/parse_fortran.py index bcc5cda3..7d2371f4 100644 --- a/fortls/parse_fortran.py +++ b/fortls/parse_fortran.py @@ -1,11 +1,10 @@ -from __future__ import print_function +from __future__ import print_function, annotations import hashlib import logging import os import re import sys -from collections import namedtuple from fortls.constants import ( DO_TYPE_ID, @@ -44,6 +43,16 @@ fortran_type, fortran_var, fortran_where, + VAR_info, + SUB_info, + FUN_info, + SELECT_info, + CLASS_info, + USE_info, + GEN_info, + SMOD_info, + INT_info, + VIS_info, ) from fortls.regex_patterns import ( ASSOCIATE_REGEX, @@ -122,26 +131,24 @@ if not PY3K: import io -# Helper types -VAR_info = namedtuple("VAR_info", ["type_word", "keywords", "var_names"]) -SUB_info = namedtuple("SUB_info", ["name", "args", "mod_flag", "keywords"]) -FUN_info = namedtuple( - "FUN_info", ["name", "args", "return_type", "return_var", "mod_flag", "keywords"] -) -SELECT_info = namedtuple("SELECT_info", ["type", "binding", "desc"]) -CLASS_info = namedtuple("CLASS_info", ["name", "parent", "keywords"]) -USE_info = namedtuple("USE_info", ["mod_name", "only_list", "rename_map"]) -GEN_info = namedtuple("GEN_info", ["bound_name", "pro_links", "vis_flag"]) -SMOD_info = namedtuple("SMOD_info", ["name", "parent"]) -INT_info = namedtuple("INT_info", ["name", "abstract"]) -VIS_info = namedtuple("VIS_info", ["type", "obj_names"]) +log = logging.getLogger(__name__) -log = logging.getLogger(__name__) +def get_line_context(line: str) -> tuple[str, None]: + """Get context of ending position in line (for completion) + Parameters + ---------- + line : str + file line -def get_line_context(line): - """Get context of ending position in line (for completion)""" + Returns + ------- + tuple[str, None] + Possible string values: + `var_key`, `pro_line`, `var_only`, `mod_mems`, `mod_only`, `pro_link`, + `skip`, `import`, `vis`, `call`, `type_only`, `int_only`, `first`, `default` + """ last_level, sections = get_paren_level(line) lev1_end = sections[-1][1] # Test if variable definition statement @@ -201,7 +208,7 @@ def get_line_context(line): return "default", None -def parse_var_keywords(test_str): +def parse_var_keywords(test_str: str) -> tuple[list[str], str]: """Parse Fortran variable declaration keywords""" keyword_match = KEYWORD_LIST_REGEX.match(test_str) keywords = [] @@ -221,7 +228,7 @@ def parse_var_keywords(test_str): return keywords, test_str -def read_var_def(line, type_word=None, fun_only=False): +def read_var_def(line: str, type_word: str = None, fun_only: bool = False): """Attempt to read variable definition line""" if type_word is None: type_match = NAT_VAR_REGEX.match(line) @@ -280,11 +287,11 @@ def read_var_def(line, type_word=None, fun_only=False): return "var", VAR_info(type_word, keywords, var_words) -def read_fun_def(line, return_type=None, mod_flag=False): +def read_fun_def(line: str, return_type=None, mod_flag: bool = False): """Attempt to read FUNCTION definition line""" mod_match = SUB_MOD_REGEX.match(line) mods_found = False - keywords = [] + keywords: list[str] = [] while mod_match is not None: mods_found = True line = line[mod_match.end(0) :] @@ -320,9 +327,9 @@ def read_fun_def(line, return_type=None, mod_flag=False): return "fun", FUN_info(name, args, return_type, return_var, mod_flag, keywords) -def read_sub_def(line, mod_flag=False): +def read_sub_def(line: str, mod_flag: bool = False): """Attempt to read SUBROUTINE definition line""" - keywords = [] + keywords: list[str] = [] mod_match = SUB_MOD_REGEX.match(line) while mod_match is not None: line = line[mod_match.end(0) :] @@ -347,7 +354,7 @@ def read_sub_def(line, mod_flag=False): return "sub", SUB_info(name, args, mod_flag, keywords) -def read_block_def(line): +def read_block_def(line: str): """Attempt to read BLOCK definition line""" block_match = BLOCK_REGEX.match(line) if block_match is not None: @@ -381,7 +388,7 @@ def read_block_def(line): return None -def read_associate_def(line): +def read_associate_def(line: str): assoc_match = ASSOCIATE_REGEX.match(line) if assoc_match is not None: trailing_line = line[assoc_match.end(0) :] @@ -392,7 +399,7 @@ def read_associate_def(line): return "assoc", var_words -def read_select_def(line): +def read_select_def(line: str): """Attempt to read SELECT definition line""" select_match = SELECT_REGEX.match(line) select_desc = None @@ -419,7 +426,7 @@ def read_select_def(line): return "select", SELECT_info(select_type, select_binding, select_desc) -def read_type_def(line): +def read_type_def(line: str): """Attempt to read TYPE definition line""" type_match = TYPE_DEF_REGEX.match(line) if type_match is None: @@ -428,7 +435,7 @@ def read_type_def(line): trailing_line = trailing_line.strip() # Parse keywords keyword_match = TATTR_LIST_REGEX.match(trailing_line) - keywords = [] + keywords: list[str] = [] parent = None while keyword_match is not None: keyword_strip = keyword_match.group(0).replace(",", " ").strip().upper() @@ -461,7 +468,7 @@ def read_type_def(line): return "typ", CLASS_info(name, parent, keywords) -def read_enum_def(line): +def read_enum_def(line: str): """Attempt to read ENUM definition line""" enum_match = ENUM_DEF_REGEX.match(line) if enum_match is not None: @@ -469,7 +476,7 @@ def read_enum_def(line): return None -def read_generic_def(line): +def read_generic_def(line: str): """Attempt to read generic procedure definition line""" generic_match = GENERIC_PRO_REGEX.match(line) if generic_match is None: @@ -490,12 +497,12 @@ def read_generic_def(line): i1 = trailing_line.find("=>") if i1 < 0: return None - bound_name = trailing_line[:i1].strip() + bound_name: str = trailing_line[:i1].strip() if GEN_ASSIGN_REGEX.match(bound_name): return None pro_list = trailing_line[i1 + 2 :].split(",") # - pro_out = [] + pro_out: list[str] = [] for bound_pro in pro_list: if len(bound_pro.strip()) > 0: pro_out.append(bound_pro.strip()) @@ -505,7 +512,7 @@ def read_generic_def(line): return "gen", GEN_info(bound_name, pro_out, vis_flag) -def read_mod_def(line): +def read_mod_def(line: str): """Attempt to read MODULE and MODULE PROCEDURE definition lines""" mod_match = MOD_REGEX.match(line) if mod_match is None: @@ -533,7 +540,7 @@ def read_mod_def(line): return "mod", name -def read_submod_def(line): +def read_submod_def(line: str): """Attempt to read SUBMODULE definition line""" submod_match = SUBMOD_REGEX.match(line) if submod_match is None: @@ -557,7 +564,7 @@ def read_submod_def(line): return "smod", SMOD_info(name, parent_name) -def read_prog_def(line): +def read_prog_def(line: str): """Attempt to read PROGRAM definition line""" prog_match = PROG_REGEX.match(line) if prog_match is None: @@ -566,7 +573,7 @@ def read_prog_def(line): return "prog", prog_match.group(1) -def read_int_def(line): +def read_int_def(line: str): """Attempt to read INTERFACE definition line""" int_match = INT_REGEX.match(line) if int_match is None: @@ -581,7 +588,7 @@ def read_int_def(line): return "int", INT_info(int_match.group(2), is_abstract) -def read_use_stmt(line): +def read_use_stmt(line: str): """Attempt to read USE statement""" use_match = USE_REGEX.match(line) if use_match is None: @@ -589,8 +596,8 @@ def read_use_stmt(line): trailing_line = line[use_match.end(0) :].lower() use_mod = use_match.group(2) - only_list = [] - rename_map = {} + only_list: list[str] = [] + rename_map: dict[str, str] = {} if use_match.group(3): for only_stmt in trailing_line.split(","): only_split = only_stmt.split("=>") @@ -601,7 +608,7 @@ def read_use_stmt(line): return "use", USE_info(use_mod, only_list, rename_map) -def read_imp_stmt(line): +def read_imp_stmt(line: str): """Attempt to read IMPORT statement""" import_match = IMPORT_REGEX.match(line) if import_match is None: @@ -612,7 +619,7 @@ def read_imp_stmt(line): return "import", import_list -def read_inc_stmt(line): +def read_inc_stmt(line: str): """Attempt to read INCLUDE statement""" inc_match = INCLUDE_REGEX.match(line) if inc_match is None: @@ -622,7 +629,7 @@ def read_inc_stmt(line): return "inc", inc_path -def read_vis_stmnt(line): +def read_vis_stmnt(line: str): """Attempt to read PUBLIC/PRIVATE statement""" vis_match = VIS_REGEX.match(line) if vis_match is None: @@ -658,14 +665,16 @@ def read_vis_stmnt(line): class fortran_file: - def __init__(self, path=None, pp_suffixes=None): - self.path = path - self.contents_split = [] - self.contents_pp = [] - self.nLines = 0 - self.fixed = False - self.ast = None - self.hash = None + def __init__(self, path: str = None, pp_suffixes: list = None): + self.path: str = path + self.contents_split: list = [] + self.contents_pp: list = [] + self.pp_defs: dict = {} + self.nLines: int = 0 + self.fixed: bool = False + self.preproc: bool = False + self.ast: fortran_ast = None + self.hash: str = None if path is not None: _, file_ext = os.path.splitext(os.path.basename(path)) if pp_suffixes is not None: @@ -675,43 +684,54 @@ def __init__(self, path=None, pp_suffixes=None): else: self.preproc = False - def copy(self): + def copy(self) -> fortran_file: """Copy content to new file object (does not copy objects)""" copy_obj = fortran_file(self.path) copy_obj.preproc = self.preproc copy_obj.fixed = self.fixed + copy_obj.contents_pp = self.contents_pp + copy_obj.contents_split = self.contents_split + copy_obj.pp_defs = self.pp_defs copy_obj.set_contents(self.contents_split) return copy_obj - def load_from_disk(self): - """Read file from disk""" + def load_from_disk(self) -> tuple[str | None, bool | None]: + """Read file from disk or update file contents only if they have changed + A MD5 hash is used to determine that + + Returns + ------- + tuple[str|None, bool|None] + `str` : string containing IO error message else None + `bool`: boolean indicating if the file has changed + """ + contents: str try: if PY3K: - with open( - self.path, "r", encoding="utf-8", errors="replace" - ) as fhandle: - contents = re.sub(r"\t", r" ", fhandle.read()) - self.hash = hashlib.md5(contents.encode("utf-8")).hexdigest() - self.contents_split = contents.splitlines() + with open(self.path, "r", encoding="utf-8", errors="replace") as f: + contents = re.sub(r"\t", r" ", f.read()) else: - with io.open( - self.path, "r", encoding="utf-8", errors="replace" - ) as fhandle: - contents = re.sub(r"\t", r" ", fhandle.read()) - self.hash = hashlib.md5(contents.encode("utf-8")).hexdigest() - self.contents_split = contents.splitlines() + with io.open(self.path, "r", encoding="utf-8", errors="replace") as f: + contents = re.sub(r"\t", r" ", f.read()) + except OSError: + return "Could not read/decode file", None + else: + # Check if files are the same + hash = hashlib.md5(contents.encode("utf-8")).hexdigest() + if hash == self.hash: + return None, False + + self.hash = hash + self.contents_split = contents.splitlines() self.fixed = detect_fixed_format(self.contents_split) self.contents_pp = self.contents_split self.nLines = len(self.contents_split) - except: - return "Could not read/decode file" - else: - return None + return None, True - def apply_change(self, change): + def apply_change(self, change: dict) -> bool: """Apply a change to the file.""" - def check_change_reparse(line_number): + def check_change_reparse(line_number: int) -> bool: if (line_number < 0) or (line_number > self.nLines - 1): return True pre_lines, curr_line, _ = self.get_code_line(line_number, forward=False) @@ -808,7 +828,7 @@ def check_change_reparse(line_number): self.set_contents(new_contents) return True - def set_contents(self, contents_split, detect_format=True): + def set_contents(self, contents_split: list, detect_format: bool = True): """Set file contents""" self.contents_split = contents_split self.contents_pp = self.contents_split @@ -816,7 +836,7 @@ def set_contents(self, contents_split, detect_format=True): if detect_format: self.fixed = detect_fixed_format(self.contents_split) - def get_line(self, line_number, pp_content=False): + def get_line(self, line_number: int, pp_content: bool = False) -> str: """Get single line from file""" try: if pp_content: @@ -828,12 +848,12 @@ def get_line(self, line_number, pp_content=False): def get_code_line( self, - line_number, - forward=True, - backward=True, - pp_content=False, - strip_comment=False, - ): + line_number: int, + forward: bool = True, + backward: bool = True, + pp_content: bool = False, + strip_comment: bool = False, + ) -> tuple[list[str], str, list[str]]: """Get full code line from file including any adjacent continuations""" curr_line = self.get_line(line_number, pp_content) if curr_line is None: @@ -933,7 +953,7 @@ def get_code_line( pre_lines.reverse() return pre_lines, curr_line, post_lines - def strip_comment(self, line): + def strip_comment(self, line: str) -> str: """Strip comment from line""" if self.fixed: if (FIXED_COMMENT_LINE_MATCH.match(line) is not None) and ( @@ -946,14 +966,19 @@ def strip_comment(self, line): return line def find_word_in_code_line( - self, line_number, find_word, forward=True, backward=False, pp_content=False - ): + self, + line_number: int, + word: str, + forward: bool = True, + backward: bool = False, + pp_content: bool = False, + ) -> tuple[int, int, int]: back_lines, curr_line, forward_lines = self.get_code_line( line_number, forward=forward, backward=backward, pp_content=pp_content ) i0 = i1 = -1 if curr_line is not None: - find_word_lower = find_word.lower() + find_word_lower = word.lower() i0, i1 = find_word_in_line(curr_line.lower(), find_word_lower) if backward and (i0 < 0): back_lines.reverse() @@ -970,8 +995,10 @@ def find_word_in_code_line( return line_number, i0, i1 return line_number, i0, i1 - def preprocess(self, pp_defs={}, include_dirs=[], debug=False): - self.contents_pp, pp_skips, pp_defines, _ = preprocess_file( + def preprocess( + self, pp_defs: dict = {}, include_dirs: list = [], debug: bool = False + ) -> tuple[list, list]: + self.contents_pp, pp_skips, pp_defines, self.pp_defs = preprocess_file( self.contents_split, self.path, pp_defs=pp_defs, @@ -1033,14 +1060,18 @@ def check_file(self, obj_tree, max_line_length=-1, max_comment_line_length=-1): def preprocess_file( - contents_split, file_path=None, pp_defs={}, include_dirs=[], debug=False + contents_split: list, + file_path: str = None, + pp_defs: dict = {}, + include_dirs: list = [], + debug: bool = False, ): # Look for and mark excluded preprocessor paths in file # Initial implementation only looks for "if" and "ifndef" statements. # For "if" statements all blocks are excluded except the "else" block if present # For "ifndef" statements all blocks excluding the first block are excluded - def eval_pp_if(text, defs={}): - def replace_ops(expr): + def eval_pp_if(text, defs: dict = {}): + def replace_ops(expr: str): expr = expr.replace("&&", " and ") expr = expr.replace("||", " or ") expr = expr.replace("!=", " <> ") @@ -1048,7 +1079,7 @@ def replace_ops(expr): expr = expr.replace(" <> ", " != ") return expr - def replace_defined(line): + def replace_defined(line: str): i0 = 0 out_line = "" for match in DEFINED_REGEX.finditer(line): @@ -1061,7 +1092,7 @@ def replace_defined(line): out_line += line[i0:] return out_line - def replace_vars(line): + def replace_vars(line: str): i0 = 0 out_line = "" for match in WORD_REGEX.finditer(line): @@ -1172,6 +1203,14 @@ def replace_vars(line): output_file.append(line) pp_defines.append(i + 1) def_name = match.group(2) + # If this is an argument list of a function add them to the name + # get_definition will only return the function name upon hover + # hence if the argument list is appended in the def_name then + # querying the dictionary will not yield a result. + # Need to properly parse the preprocessor files instead of this. + # This also does not allow for multiline argument list definitions. + # if match.group(3): + # def_name += match.group(3) if (match.group(1) == "define") and (def_name not in defs_tmp): eq_ind = line[match.end(0) :].find(" ") if eq_ind >= 0: @@ -1204,7 +1243,7 @@ def replace_vars(line): if include_path is not None: try: include_file = fortran_file(include_path) - err_string = include_file.load_from_disk() + err_string, _ = include_file.load_from_disk() if err_string is None: log.debug(f'\n!!! Parsing include file "{include_path}"') _, _, _, defs_tmp = preprocess_file( @@ -1239,7 +1278,12 @@ def replace_vars(line): return output_file, pp_skips, pp_defines, defs_tmp -def process_file(file_obj, close_open_scopes, debug=False, pp_defs={}, include_dirs=[]): +def process_file( + file_obj: fortran_file, + debug: bool = False, + pp_defs: dict = {}, + include_dirs: list = [], +): """Build file AST by parsing file""" def parser_debug_msg(msg: str, line: str, ln: int): diff --git a/fortls/regex_patterns.py b/fortls/regex_patterns.py index 781ce922..7ef1e193 100644 --- a/fortls/regex_patterns.py +++ b/fortls/regex_patterns.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import re +from typing import Pattern USE_REGEX = re.compile( r"[ ]*USE([, ]+(?:INTRINSIC|NON_INTRINSIC))?[ :]+(\w*)([, ]+ONLY[ :]+)?", @@ -105,7 +108,7 @@ # Preprocessor mathching rules DEFINED_REGEX = re.compile(r"defined[ ]*\([ ]*([a-z_][a-z0-9_]*)[ ]*\)", re.I) PP_REGEX = re.compile(r"#(if |ifdef|ifndef|else|elif|endif)") -PP_DEF_REGEX = re.compile(r"#(define|undef)[ ]*([a-z0-9_]+)", re.I) +PP_DEF_REGEX = re.compile(r"#(define|undef)[ ]*([\w]+)(\((\w+(,[ ]*)?)+\))?", re.I) PP_DEF_TEST_REGEX = re.compile(r"(![ ]*)?defined[ ]*\([ ]*([a-z0-9_]*)[ ]*\)$", re.I) PP_INCLUDE_REGEX = re.compile(r"#include[ ]*([\"a-z0-9_\.]*)", re.I) # Context matching rules @@ -125,3 +128,32 @@ CLASS_VAR_REGEX = re.compile(r"(TYPE|CLASS)[ ]*\(", re.I) DEF_KIND_REGEX = re.compile(r"([a-z]*)[ ]*\((?:KIND|LEN)?[ =]*([a-z_]\w*)", re.I) OBJBREAK_REGEX = re.compile(r"[\/\-(.,+*<>=$: ]", re.I) + + +def src_file_exts(input_exts: list[str] = []) -> Pattern[str]: + """Create a REGEX for which file extensions the Language Server should parse + Default extensions are + F F03 F05 F08 F18 F77 F90 F95 FOR FPP f f03 f05 f08 f18 f77 f90 f95 for fpp + + Parameters + ---------- + input_exts : list[str], optional + Additional Fortran, by default [] + + Returns + ------- + Pattern[str] + A compiled regular expression, by default + '.(F|F03|F05|F08|F18|F77|F90|F95|FOR|FPP|f|f03|f05|f08|f18|f77|f90|f95|for|fpp)?' + """ + EXTS = ["", "77", "90", "95", "03", "05", "08", "18", "OR", "PP"] + FORTRAN_FILE_EXTS = [] + for e in EXTS: + FORTRAN_FILE_EXTS.extend([f"F{e}".upper(), f"f{e}".lower()]) + # Add the custom extensions for the server to parse + for e in input_exts: + if e.startswith("."): + FORTRAN_FILE_EXTS.append(e.replace(".", "")) + # Cast into a set to ensure uniqueness of extensions & sort for consistency + # Create a regular expression from this + return re.compile(fr"\.({'|'.join(sorted(set(FORTRAN_FILE_EXTS)))})?$") diff --git a/fortls/statements.json b/fortls/statements.json index 4ee8b441..94a82e5e 100644 --- a/fortls/statements.json +++ b/fortls/statements.json @@ -3,8 +3,8 @@ "CHARACTER": { "args": "LEN=len" }, "CLASS": { "args": "name" }, "COMPLEX": { "args": "KIND=kind" }, - "DOUBLE COMPLEX": { }, - "DOUBLE PRECISION": { }, + "DOUBLE COMPLEX": {}, + "DOUBLE PRECISION": {}, "INTEGER": { "args": "KIND=kind" }, "LOGICAL": { "args": "KIND=kind" }, "REAL": { "args": "KIND=kind" }, diff --git a/test/test_preproc.py b/test/test_preproc.py new file mode 100644 index 00000000..8037a07f --- /dev/null +++ b/test/test_preproc.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import os +from setup_tests import ( + run_request, + # path_to_uri, + # write_rpc_notification, + write_rpc_request, + test_dir, +) + + +def test_hover(): + def hover_req(file_path: str, ln: int, col: int) -> str: + return write_rpc_request( + 1, + "textDocument/hover", + { + "textDocument": {"uri": file_path}, + "position": {"line": ln, "character": col}, + }, + ) + + def check_return(result_array, checks): + assert len(result_array) == len(checks) + for (i, check) in enumerate(checks): + assert result_array[i]["contents"][0]["value"] == check + + root_dir = os.path.join(test_dir, "pp") + string = write_rpc_request(1, "initialize", {"rootPath": root_dir}) + file_path = os.path.join(test_dir, "pp", "preproc.F90") + string += hover_req(file_path, 5, 8) # user defined type + string += hover_req(file_path, 7, 30) # variable + string += hover_req(file_path, 7, 40) # multi-lin variable + string += hover_req(file_path, 8, 7) # function with if conditional + string += hover_req(file_path, 9, 7) # multiline function with if conditional + errcode, results = run_request(string, f" --config={root_dir}/.pp_conf.json") + assert errcode == 0 + + # Reference solution + ref_results = ( + "#define PCType character*(80)", + "#define PETSC_ERR_INT_OVERFLOW 84", + "#define varVar 55", + "#define ewrite if (priority <= 3) write((priority), format)", + "#define ewrite2 if (priority <= 3) write((priority), format)", + ) + assert len(ref_results) == len(results) - 1 + check_return(results[1:], ref_results) diff --git a/test/test_server.py b/test/test_server.py index fa47c625..168159fa 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -279,7 +279,7 @@ def comp_request(file_path, line, char): exp_results = ( # test_prog.f08 [1, "myfun", "DOUBLE PRECISION FUNCTION myfun(n, xval)"], - [4, "glob_sub", "SUBROUTINE glob_sub(n, xval, yval)"], + [9, "glob_sub", "SUBROUTINE glob_sub(n, xval, yval)"], [1, "bound_nopass", "SUBROUTINE bound_nopass(a, b)"], [1, "bound_pass", "SUBROUTINE bound_pass(arg1)"], [1, "stretch_vector", "TYPE(scaled_vector)"], @@ -310,16 +310,16 @@ def comp_request(file_path, line, char): [1, "n", "INTEGER(4)"], [2, "a", "REAL(8)"], # test_block.f08 - [7, "READ", "STATEMENT"], - [8, "READ", "STATEMENT"], [9, "READ", "STATEMENT"], + [10, "READ", "STATEMENT"], + [11, "READ", "STATEMENT"], # subdir/test_generic.f90 [4, "my_gen", "SUBROUTINE my_gen(self, a, b)"], # subdir/test_inherit.f90 [1, "val", "REAL(8)"], # subdir/test_rename.F90 [1, "localname", "INTEGER"], - [1, "renamed_var2", "REAL(8)"], + [2, "renamed_var2", "REAL(8)"], # subdir/test_vis.f90 [3, "some_type", "TYPE"], # test_import.f90 @@ -401,6 +401,7 @@ def def_request(file_path, line, char): string += def_request(file_path, 30, 12) string += def_request(file_path, 35, 12) file_path = os.path.join(test_dir, "test_inc.f90") + string += def_request(file_path, 2, 15) string += def_request(file_path, 10, 2) file_path = os.path.join(test_dir, "subdir", "test_inc2.f90") string += def_request(file_path, 3, 2) @@ -424,6 +425,7 @@ def def_request(file_path, line, char): [1, 1, os.path.join(test_dir, "subdir", "test_submod.F90")], [1, 1, os.path.join(test_dir, "subdir", "test_submod.F90")], # test_inc.f90 + [2, 2, os.path.join(test_dir, "subdir", "test_inc2.f90")], [0, 0, os.path.join(test_dir, "subdir", "test_inc2.f90")], # subdir/test_inc2.f90 [4, 4, os.path.join(test_dir, "test_inc.f90")], diff --git a/test/test_source/pp/.fortls b/test/test_source/pp/.fortls new file mode 120000 index 00000000..a0f977d0 --- /dev/null +++ b/test/test_source/pp/.fortls @@ -0,0 +1 @@ +.pp_conf.json \ No newline at end of file diff --git a/test/test_source/pp/.pp_conf.json b/test/test_source/pp/.pp_conf.json new file mode 100644 index 00000000..90bda382 --- /dev/null +++ b/test/test_source/pp/.pp_conf.json @@ -0,0 +1,10 @@ +{ + "lowercase_intrinsics": true, + "use_signature_help": true, + "variable_hover": true, + "hover_signature": true, + "enable_code_actions": true, + "pp_suffixes": [".h", ".F90"], + "incl_suffixes": [".h"], + "include_dirs": ["include"] +} diff --git a/test/test_source/pp/include/petscerror.h b/test/test_source/pp/include/petscerror.h new file mode 100644 index 00000000..b1b500dd --- /dev/null +++ b/test/test_source/pp/include/petscerror.h @@ -0,0 +1,7 @@ +#if !defined (PETSCERRORDEF_H) +#define PETSCERRORDEF_H + +#define PETSC_ERR_MEM 55 +#define PETSC_ERR_INT_OVERFLOW 84 +#define PETSC_ERR_FLOP_COUNT 90 +#endif diff --git a/test/test_source/pp/include/petscpc.h b/test/test_source/pp/include/petscpc.h new file mode 100644 index 00000000..b57881b3 --- /dev/null +++ b/test/test_source/pp/include/petscpc.h @@ -0,0 +1,13 @@ +#if !defined (PETSCPCDEF_H) +#define PETSCPCDEF_H + +#include "petscerror.h" + +#define PC type(tPC) +#define PCType character*(80) +#define ewrite(priority, format) if (priority <= 3) write((priority), format) +#define ewrite2(priority, format) \ + if (priority <= 3) write((priority), format) +#define varVar \ + 55 +#endif diff --git a/test/test_source/pp/preproc.F90 b/test/test_source/pp/preproc.F90 new file mode 100644 index 00000000..8da59d9c --- /dev/null +++ b/test/test_source/pp/preproc.F90 @@ -0,0 +1,13 @@ +program preprocessor + +#include "petscpc.h" +#ifdef PETSCPCDEF_H + integer, parameter :: var = 1000 + PCType :: tmp + print*, 999, 3.14, "some", var, PETSC_ERR_MEM + print*, PETSC_ERR_INT_OVERFLOW, varVar + ewrite(1,*) 'Assemble EP P1 matrix and rhs sytem' + ewrite2(1,*) 'Assemble EP P1 matrix and rhs sytem' + +#endif +end program preprocessor \ No newline at end of file