rapydscript-ng 0.7.24 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +80 -26
  3. package/bin/build.ts +117 -0
  4. package/bin/package.json +1 -0
  5. package/bin/rapydscript +65 -62
  6. package/build_wheels.py +133 -0
  7. package/editor-plugins/README.md +80 -0
  8. package/editor-plugins/nvim.lua +321 -0
  9. package/local-agent.md +28 -0
  10. package/package.json +4 -5
  11. package/publish.py +27 -17
  12. package/release/baselib-plain-pretty.js +448 -326
  13. package/release/baselib-plain-ugly.js +3 -3
  14. package/release/compiler.js +9027 -8139
  15. package/release/signatures.json +27 -27
  16. package/release/stdlib_modules.json +1 -0
  17. package/src/ast.pyj +428 -280
  18. package/src/baselib-builtins.pyj +49 -28
  19. package/src/baselib-containers.pyj +241 -226
  20. package/src/baselib-errors.pyj +8 -1
  21. package/src/baselib-internal.pyj +38 -23
  22. package/src/baselib-itertools.pyj +13 -7
  23. package/src/baselib-str.pyj +59 -84
  24. package/src/compiler.pyj +4 -4
  25. package/src/errors.pyj +3 -4
  26. package/src/lib/aes.pyj +46 -32
  27. package/src/lib/elementmaker.pyj +11 -9
  28. package/src/lib/encodings.pyj +15 -5
  29. package/src/lib/gettext.pyj +9 -4
  30. package/src/lib/math.pyj +106 -45
  31. package/src/lib/operator.pyj +10 -10
  32. package/src/lib/pythonize.pyj +2 -1
  33. package/src/lib/random.pyj +28 -21
  34. package/src/lib/re.pyj +492 -19
  35. package/src/lib/traceback.pyj +171 -29
  36. package/src/lib/uuid.pyj +2 -2
  37. package/src/output/classes.pyj +28 -27
  38. package/src/output/codegen.pyj +87 -83
  39. package/src/output/comments.pyj +8 -8
  40. package/src/output/exceptions.pyj +20 -21
  41. package/src/output/functions.pyj +66 -64
  42. package/src/output/literals.pyj +24 -23
  43. package/src/output/loops.pyj +84 -142
  44. package/src/output/modules.pyj +55 -68
  45. package/src/output/operators.pyj +40 -29
  46. package/src/output/statements.pyj +21 -15
  47. package/src/output/stream.pyj +82 -56
  48. package/src/output/utils.pyj +12 -8
  49. package/src/parse.pyj +971 -620
  50. package/src/string_interpolation.pyj +5 -3
  51. package/src/tokenizer.pyj +188 -148
  52. package/src/utils.pyj +32 -15
  53. package/test/_treeshake_mod.pyj +30 -0
  54. package/test/_treeshake_side_effect.pyj +9 -0
  55. package/test/ast_serialization.pyj +392 -0
  56. package/test/async.pyj +95 -0
  57. package/test/baselib.pyj +1 -2
  58. package/test/embedded_compiler.pyj +57 -0
  59. package/test/fmt.pyj +291 -0
  60. package/test/generic.pyj +16 -3
  61. package/test/imports.pyj +8 -6
  62. package/test/internationalization.pyj +38 -37
  63. package/test/lint.pyj +120 -117
  64. package/test/lsp.pyj +257 -0
  65. package/test/repl.pyj +70 -65
  66. package/test/sourcemaps.pyj +315 -0
  67. package/test/starargs.pyj +46 -39
  68. package/test/str.pyj +8 -0
  69. package/test/traceback.pyj +263 -0
  70. package/test/treeshaking.pyj +181 -0
  71. package/test/web_repl_export.pyj +88 -0
  72. package/tools/ast_serialize.mjs +557 -0
  73. package/tools/cli.mjs +531 -0
  74. package/tools/compile.mjs +205 -0
  75. package/tools/compiler.mjs +159 -0
  76. package/tools/{completer.js → completer.mjs} +6 -6
  77. package/tools/{embedded_compiler.js → embedded_compiler.mjs} +17 -13
  78. package/tools/export.js +109 -56
  79. package/tools/fmt.mjs +975 -0
  80. package/tools/{gettext.js → gettext.mjs} +46 -53
  81. package/tools/ini.mjs +175 -0
  82. package/tools/{lint.js → lint.mjs} +78 -55
  83. package/tools/lsp.mjs +964 -0
  84. package/tools/lsp_protocol.mjs +259 -0
  85. package/tools/lsp_symbols.mjs +418 -0
  86. package/tools/{msgfmt.js → msgfmt.mjs} +18 -18
  87. package/tools/{repl.js → repl.mjs} +56 -42
  88. package/tools/{self.js → self.mjs} +71 -46
  89. package/tools/sourcemap.mjs +123 -0
  90. package/tools/test.mjs +252 -0
  91. package/tools/treeshake.mjs +400 -0
  92. package/tools/{utils.js → utils.mjs} +26 -23
  93. package/tools/{web_repl.js → web_repl.mjs} +15 -13
  94. package/tools/web_repl_export.mjs +97 -0
  95. package/tree-sitter/README.md +101 -0
  96. package/tree-sitter/grammar.js +992 -0
  97. package/tree-sitter/package.json +43 -0
  98. package/tree-sitter/queries/highlights.scm +232 -0
  99. package/tree-sitter/queries/indents.scm +64 -0
  100. package/tree-sitter/queries/injections.scm +14 -0
  101. package/tree-sitter/queries/locals.scm +108 -0
  102. package/tree-sitter/src/grammar.json +4976 -0
  103. package/tree-sitter/src/node-types.json +2901 -0
  104. package/tree-sitter/src/parser.c +196465 -0
  105. package/tree-sitter/src/scanner.c +294 -0
  106. package/tree-sitter/src/tree_sitter/alloc.h +54 -0
  107. package/tree-sitter/src/tree_sitter/array.h +330 -0
  108. package/tree-sitter/src/tree_sitter/parser.h +286 -0
  109. package/tree-sitter/test/corpus/chaining.txt +99 -0
  110. package/tree-sitter/test/corpus/classes.txt +147 -0
  111. package/tree-sitter/test/corpus/comprehensions.txt +155 -0
  112. package/tree-sitter/test/corpus/containers.txt +242 -0
  113. package/tree-sitter/test/corpus/control_flow.txt +361 -0
  114. package/tree-sitter/test/corpus/decorators.txt +64 -0
  115. package/tree-sitter/test/corpus/existential.txt +102 -0
  116. package/tree-sitter/test/corpus/functions.txt +293 -0
  117. package/tree-sitter/test/corpus/imports.txt +117 -0
  118. package/tree-sitter/test/corpus/literals.txt +135 -0
  119. package/tree-sitter/test/corpus/operators.txt +296 -0
  120. package/tree-sitter/test/corpus/statements.txt +189 -0
  121. package/tree-sitter/test/corpus/strings.txt +90 -0
  122. package/tree-sitter/test/corpus/subscripts.txt +227 -0
  123. package/tree-sitter/tree-sitter.json +36 -0
  124. package/web-repl/env.js +35 -23
  125. package/web-repl/main.js +8 -8
  126. package/bin/export +0 -75
  127. package/bin/web-repl-export +0 -102
  128. package/tools/cli.js +0 -523
  129. package/tools/compile.js +0 -184
  130. package/tools/compiler.js +0 -84
  131. package/tools/ini.js +0 -65
  132. package/tools/test.js +0 -110
@@ -1,12 +1,14 @@
1
1
  /* vim:fileencoding=utf-8
2
- *
2
+ *
3
3
  * Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
4
  *
5
5
  * Distributed under terms of the BSD license
6
6
  */
7
- "use strict"; /*jshint node:true */
7
+ "use strict";
8
+
9
+ import fs from 'fs';
10
+ import path from 'path';
8
11
 
9
- var comment_contents = /\/\*!?(?:\@preserve)?[ \t]*(?:\r\n|\n)([\s\S]*?)(?:\r\n|\n)[ \t]*\*\//;
10
12
  var colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
11
13
 
12
14
  function ansi(code) {
@@ -14,13 +16,13 @@ function ansi(code) {
14
16
  return String.fromCharCode(27) + '[' + code + 'm';
15
17
  }
16
18
 
17
- function path_exists(path) {
18
- var fs = require('fs');
19
+ async function path_exists(p) {
19
20
  try {
20
- fs.statSync(path);
21
+ await fs.promises.stat(p);
21
22
  return true;
22
23
  } catch(e) {
23
24
  if (e.code != 'ENOENT') throw e;
25
+ return false;
24
26
  }
25
27
  }
26
28
 
@@ -57,7 +59,7 @@ function supports_color(stdout) {
57
59
 
58
60
  }
59
61
 
60
- function safe_colored(string) {
62
+ function passthrough_colored(string) {
61
63
  return string;
62
64
  }
63
65
 
@@ -82,11 +84,11 @@ function wrap(lines, width) {
82
84
  line = prev + line;
83
85
  prev = '';
84
86
  if (line.length > width) {
85
- prev = line.substr(width);
87
+ prev = line.substr(width - 1);
86
88
  if (prev) prev += ' ';
87
89
  line = line.substr(0, width - 1);
88
- if (line.substr(line.length - 1 !== ' ')) line += '-';
89
- }
90
+ if (line.substr(line.length - 1) !== ' ') line += '-';
91
+ }
90
92
  ans.push(line);
91
93
  });
92
94
  if (prev) ans = ans.concat(wrap([prev]));
@@ -105,24 +107,25 @@ function merge() {
105
107
  }
106
108
 
107
109
  function get_import_dirs(paths_string, ignore_env) {
108
- var path = require('path');
109
110
  var paths = [];
110
- function merge(new_path) {
111
+ function add(new_path) {
111
112
  if (paths.indexOf(new_path) == -1) paths.push(new_path);
112
113
  }
113
114
  if (!ignore_env && process && process.env && process.env.RAPYDSCRIPT_IMPORT_PATH) {
114
- process.env.RAPYDSCRIPT_IMPORT_PATH.split(path.delimiter).forEach(merge);
115
+ process.env.RAPYDSCRIPT_IMPORT_PATH.split(path.delimiter).forEach(add);
115
116
  }
116
- if (paths_string) paths_string.split(path.delimiter).forEach(merge);
117
+ if (paths_string) paths_string.split(path.delimiter).forEach(add);
117
118
  return paths;
118
119
  }
119
120
 
120
- exports.comment_contents = comment_contents;
121
- exports.repeat = repeat;
122
- exports.wrap = wrap;
123
- exports.merge = merge;
124
- exports.colored = colored;
125
- exports.safe_colored = (supports_color()) ? colored : safe_colored;
126
- exports.generators_available = generators_available;
127
- exports.get_import_dirs = get_import_dirs;
128
- exports.path_exists = path_exists;
121
+ export {
122
+ repeat,
123
+ wrap,
124
+ merge,
125
+ colored,
126
+ generators_available,
127
+ get_import_dirs,
128
+ path_exists,
129
+ };
130
+
131
+ export var safe_colored = (supports_color()) ? colored : passthrough_colored;
@@ -1,18 +1,21 @@
1
1
  /* vim:fileencoding=utf-8
2
- *
2
+ *
3
3
  * Copyright (C) 2016 Kovid Goyal <kovid at kovidgoyal.net>
4
4
  *
5
5
  * Distributed under terms of the BSD license
6
6
  */
7
- "use strict"; /*jshint node:true */
8
- var vm = require('vm');
9
- var embedded_compiler = require('tools/embedded_compiler.js');
7
+ "use strict";
8
+
9
+ import vm from 'vm';
10
+ import embedded_compiler from './embedded_compiler.mjs';
11
+ import tree_shake from './treeshake.mjs';
12
+ import { generate_source_map } from './sourcemap.mjs';
10
13
 
11
- module.exports = function(compiler, baselib) {
14
+ export default async function(compiler, baselib) {
12
15
  var ctx = vm.createContext();
13
16
  var LINE_CONTINUATION_CHARS = ':\\';
14
17
  var find_completions = null;
15
- var streaming_compiler = embedded_compiler(compiler, baselib, function(js) { return vm.runInContext(js, ctx); }, '__repl__');
18
+ var streaming_compiler = await embedded_compiler(compiler, baselib, function(js) { return vm.runInContext(js, ctx); }, '__repl__', tree_shake, generate_source_map);
16
19
 
17
20
  return {
18
21
  'in_block_mode': false,
@@ -20,13 +23,13 @@ module.exports = function(compiler, baselib) {
20
23
  'replace_print': function replace_print(write_line_func) {
21
24
  ctx.print = function() {
22
25
  var parts = [];
23
- for (var i = 0; i < arguments.length; i++)
26
+ for (var i = 0; i < arguments.length; i++)
24
27
  parts.push(ctx.ρσ_str(arguments[i]));
25
28
  write_line_func(parts.join(' '));
26
29
  };
27
30
  },
28
31
 
29
- 'is_input_complete': function is_input_complete(source) {
32
+ 'is_input_complete': async function is_input_complete(source) {
30
33
  if (!source || !source.trim()) return false;
31
34
  var lines = source.split('\n');
32
35
  var last_line = lines[lines.length - 1].trimRight();
@@ -45,7 +48,7 @@ module.exports = function(compiler, baselib) {
45
48
  return false;
46
49
  }
47
50
  try {
48
- compiler.parse(source, {'filename': '<repl>', 'basedir': '__stdlib__'});
51
+ await compiler.parse(source, {'filename': '<repl>', 'basedir': '__stdlib__'});
49
52
  } catch(e) {
50
53
  if (e.is_eof && e.line === lines.length && e.col > 0) {
51
54
  return false;
@@ -57,11 +60,11 @@ module.exports = function(compiler, baselib) {
57
60
  return true;
58
61
  },
59
62
 
60
- 'compile': function web_repl_compile(code, opts) {
63
+ 'compile': async function web_repl_compile(code, opts) {
61
64
  opts = opts || {};
62
65
  opts.keep_docstrings = true;
63
66
  opts.filename = '<input>';
64
- return streaming_compiler.compile(code, opts);
67
+ return await streaming_compiler.compile(code, opts);
65
68
  },
66
69
 
67
70
  'runjs': function runjs(code) {
@@ -83,5 +86,4 @@ module.exports = function(compiler, baselib) {
83
86
  },
84
87
 
85
88
  };
86
- };
87
-
89
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ * web_repl_export.mjs
3
+ * Copyright (C) 2015 Kovid Goyal <kovid at kovidgoyal.net>
4
+ *
5
+ * Distributed under terms of the BSD license.
6
+ */
7
+
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import * as utils from './utils.mjs';
11
+
12
+ export default async function run_web_repl_export(base_path, lib_path, argv) {
13
+ if (globalThis.__rapydscript_embedded__) {
14
+ console.error('web-repl-export requires the full source repository and cannot run from a standalone binary.');
15
+ process.exit(1);
16
+ }
17
+ var output_dir = argv.files[0];
18
+ if (!output_dir) {
19
+ console.error('Usage: rapydscript web-repl-export /path/to/export/directory');
20
+ process.exit(1);
21
+ }
22
+
23
+ var meta = JSON.parse(await fs.promises.readFile(path.join(base_path, 'package.json'), 'utf-8'));
24
+ var commit_sha = (await fs.promises.readFile(path.join(base_path, '.git', 'refs', 'heads', 'master'), 'utf-8')).trim();
25
+
26
+ var manifest = {}, total = 0;
27
+ for (const x of ['compiler.js', 'baselib-plain-pretty.js']) {
28
+ manifest[x] = await fs.promises.readFile(path.join(lib_path, x), 'utf-8');
29
+ total += manifest[x].length;
30
+ }
31
+
32
+ for (const x of ['web_repl.js', 'embedded_compiler.js', 'treeshake.js', 'sourcemap.js', 'utils.js', 'completer.js', 'msgfmt.js', 'gettext.js', 'ast_serialize.mjs']) {
33
+ var mjs = x.replace(/\.js$/, '.mjs');
34
+ var mjs_path = path.join(base_path, 'tools', mjs);
35
+ var js_path = path.join(base_path, 'tools', x);
36
+ var use_mjs = await utils.path_exists(mjs_path);
37
+ var key = 'tools/' + (use_mjs ? mjs : x);
38
+ manifest[key] = await fs.promises.readFile(use_mjs ? mjs_path : js_path, 'utf-8');
39
+ total += manifest[key].length;
40
+ }
41
+
42
+ var stdlib = path.join(base_path, 'src', 'lib');
43
+
44
+ async function process_stdlib_dir(relpath) {
45
+ var fullpath = relpath ? path.join(stdlib, relpath) : stdlib;
46
+ var entries = await fs.promises.readdir(fullpath);
47
+ for (const x of entries) {
48
+ var q = path.join(fullpath, x);
49
+ var s = await fs.promises.stat(q);
50
+ if (s.isDirectory()) { await process_stdlib_dir(relpath + '/' + x); continue; }
51
+ if (!x.endsWith('.pyj')) continue;
52
+ var iname = path.normalize('__stdlib__' + '/' + relpath + '/' + x);
53
+ var raw = await fs.promises.readFile(q, 'utf-8');
54
+ manifest[iname] = raw;
55
+ total += s.size;
56
+ }
57
+ }
58
+ await process_stdlib_dir('');
59
+
60
+ var rs = '// vim:fileencoding=utf-8\n';
61
+ rs += '(function(external_namespace) {\n';
62
+ rs += '"use strict;"\n';
63
+ rs += 'var rs_version = ' + JSON.stringify(meta.version) + ';\n';
64
+ rs += 'var rs_commit_sha = ' + JSON.stringify(commit_sha) + ';\n';
65
+ rs += '\n// Embedded modules {{{\n';
66
+ rs += 'var data = ' + JSON.stringify(manifest) + ';\n\n';
67
+ rs += '// End embedded modules }}}\n\n';
68
+ rs += await fs.promises.readFile(path.join(base_path, 'web-repl', 'env.js'));
69
+ rs += '\n// Embedded sha1 implementation {{{\n';
70
+ rs += '(function() {\n';
71
+ rs += await fs.promises.readFile(path.join(base_path, 'web-repl', 'sha1.js'));
72
+ rs += '}).call(jsSHA);\n';
73
+ rs += '// End embedded sha1 implementation }}}\n\n';
74
+ rs += 'var exports = namespace;\n';
75
+ rs += await fs.promises.readFile(path.join(base_path, 'tools', 'export.js'), 'utf-8');
76
+ rs += 'external_namespace.RapydScript = namespace;\n';
77
+ rs += '})(this);\n';
78
+
79
+ var base_dir = path.normalize(path.resolve(output_dir));
80
+
81
+ try {
82
+ await fs.promises.mkdir(base_dir);
83
+ } catch(e) {
84
+ if (e.code !== 'EEXIST') throw e;
85
+ }
86
+
87
+ await fs.promises.writeFile(path.join(base_dir, 'rapydscript.js'), rs, 'utf-8');
88
+ var web_repl_dir = path.join(base_path, 'web-repl');
89
+ var web_repl_files = await fs.promises.readdir(web_repl_dir);
90
+ for (const x of web_repl_files) {
91
+ if (['sha1.js', 'env.js'].indexOf(x) !== -1) continue;
92
+ var file_data = await fs.promises.readFile(path.join(web_repl_dir, x), 'utf-8');
93
+ await fs.promises.writeFile(path.join(base_dir, x), file_data, 'utf-8');
94
+ }
95
+ console.log('RapydScript compiler (uncompressed) size: ' + (total/(1024)).toFixed(1) + ' KB');
96
+ console.log('web-repl exported to: ' + base_dir);
97
+ }
@@ -0,0 +1,101 @@
1
+ # tree-sitter-rapydscript
2
+
3
+ A [tree-sitter](https://tree-sitter.github.io/tree-sitter/) grammar for
4
+ [RapydScript](../README.md) (`.pyj` files).
5
+
6
+ RapydScript is a Python-like language that compiles to JavaScript. This grammar
7
+ covers the language as implemented by `rapydscript-ng`, including the features
8
+ that distinguish it from Python:
9
+
10
+ - **multi-line anonymous functions** declared with `def(...)` / `async def(...)`
11
+ (RapydScript's answer to Python's single-expression `lambda`), both inline
12
+ (`f = def(a, b): return a + b`) and with an indented block body;
13
+ - the **existential operator** `?` in all its forms — `a?` (existence check),
14
+ `a?.b` / `a?[i]` / `a?()` (safe access), and `a ? b` (default operator);
15
+ - **verbatim JavaScript** literals via the `v` string modifier (`v'...'`) and
16
+ the native `for v'i=0; i<n; i++':` loop;
17
+ - JavaScript **regular-expression literals**, including the verbose
18
+ `/// ... ///` form;
19
+ - `do: ... .while cond` loops;
20
+ - **scoped compiler flags** (`from __python__ import dict_literals, ...`).
21
+
22
+ It deliberately does **not** accept constructs that RapydScript itself rejects,
23
+ such as the walrus operator `:=`, `match`/`case`, or `*`-unpacking in assignment
24
+ targets.
25
+
26
+ It **does** handle the trickier corners of the language, including assignment as
27
+ an expression (`if m = re.exec(x):`, `a[i -= 1]`), keyword-vs-assignment
28
+ disambiguation in calls, named function *expressions* (`x = def named(a): ...`),
29
+ typed `*args`/`**kwargs`, bare generator arguments (`sum(x for x in xs)`),
30
+ multi-line anonymous-function arguments with the comma-on-the-next-line idiom,
31
+ and comma-first dict literals whose values are multi-line functions.
32
+
33
+ ### Known limitations
34
+
35
+ A few rarely-used constructs — found only in the RapydScript compiler's own
36
+ source and standard library, not in ordinary programs — are not parsed. Every
37
+ file under `test/` (representative user code) and every example in the language
38
+ [README](../README.md) parses cleanly; the exceptions are:
39
+
40
+ - the colon-less one-line `if`: `if (cond) do_something()`;
41
+ - chained assignment whose value is a multi-line-block anonymous function:
42
+ `a = b = def(): <block>`;
43
+ - a ternary used as the return value of an *inline* anonymous function that is
44
+ itself a call argument, with a trailing `;`.
45
+
46
+ In these cases tree-sitter's error recovery still highlights the surrounding
47
+ code.
48
+
49
+ ## Layout
50
+
51
+ ```
52
+ tree-sitter/
53
+ ├── grammar.js # the grammar
54
+ ├── tree-sitter.json # grammar metadata (CLI)
55
+ ├── package.json # npm package metadata
56
+ ├── src/
57
+ │ ├── parser.c # generated parser (checked in)
58
+ │ ├── scanner.c # external scanner: indentation + regex literals
59
+ │ ├── grammar.json # generated
60
+ │ └── node-types.json # generated
61
+ ├── queries/
62
+ │ ├── highlights.scm # syntax highlighting
63
+ │ └── injections.scm # embedded-language injections
64
+ └── test/corpus/ # tree-sitter test suite
65
+ ```
66
+
67
+ ## Building & testing
68
+
69
+ From this directory:
70
+
71
+ ```sh
72
+ tree-sitter generate # regenerate src/parser.c from grammar.js
73
+ tree-sitter test # run the corpus tests in test/corpus/
74
+ tree-sitter parse FILE # parse a .pyj file and print its syntax tree
75
+ tree-sitter build # compile the parser (+ scanner) into a shared object
76
+ ```
77
+
78
+ ## Using it in Neovim
79
+
80
+ With [nvim-treesitter](https://github.com/nvim-treesitter/nvim-treesitter),
81
+ register the parser and map the `.pyj` extension to it:
82
+
83
+ ```lua
84
+ local rapyd_repo_path = vim.fn.expand("/path/to/rapydscript/tree-sitter")
85
+ vim.api.nvim_create_autocmd('User', {
86
+ pattern = 'TSUpdate',
87
+ callback = function()
88
+ require("nvim-treesitter.parsers").rapydscript = {
89
+ install_info = {
90
+ path = rapyd_repo_path,
91
+ files = { "src/parser.c", "src/scanner.c" },
92
+ },
93
+ filetype = "rapydscript",
94
+ }
95
+ end
96
+ })
97
+ vim.opt.rtp:append(rapyd_repo_path)
98
+ vim.filetype.add({ extension = { pyj = "rapydscript" } })
99
+ ```
100
+
101
+ Then `:TSInstall rapydscript`.