@ugo-studio/jspp 0.1.2 → 0.1.4

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 (72) hide show
  1. package/README.md +5 -3
  2. package/dist/analysis/scope.js +38 -15
  3. package/dist/analysis/typeAnalyzer.js +257 -23
  4. package/dist/ast/types.js +6 -0
  5. package/dist/cli.js +3 -4
  6. package/dist/core/codegen/class-handlers.js +127 -0
  7. package/dist/core/codegen/control-flow-handlers.js +464 -0
  8. package/dist/core/codegen/declaration-handlers.js +31 -14
  9. package/dist/core/codegen/expression-handlers.js +432 -116
  10. package/dist/core/codegen/function-handlers.js +110 -13
  11. package/dist/core/codegen/helpers.js +76 -8
  12. package/dist/core/codegen/index.js +18 -5
  13. package/dist/core/codegen/literal-handlers.js +3 -0
  14. package/dist/core/codegen/statement-handlers.js +152 -186
  15. package/dist/core/codegen/visitor.js +35 -3
  16. package/package.json +3 -3
  17. package/src/prelude/any_value.hpp +658 -734
  18. package/src/prelude/any_value_access.hpp +103 -0
  19. package/src/prelude/any_value_defines.hpp +151 -0
  20. package/src/prelude/any_value_helpers.hpp +246 -0
  21. package/src/prelude/exception.hpp +31 -0
  22. package/src/prelude/exception_helpers.hpp +49 -0
  23. package/src/prelude/index.hpp +35 -12
  24. package/src/prelude/library/console.hpp +20 -20
  25. package/src/prelude/library/error.hpp +111 -0
  26. package/src/prelude/library/global.hpp +15 -4
  27. package/src/prelude/library/performance.hpp +25 -0
  28. package/src/prelude/library/promise.hpp +121 -0
  29. package/src/prelude/library/symbol.hpp +60 -4
  30. package/src/prelude/library/timer.hpp +92 -0
  31. package/src/prelude/scheduler.hpp +145 -0
  32. package/src/prelude/types.hpp +33 -6
  33. package/src/prelude/utils/access.hpp +174 -0
  34. package/src/prelude/utils/log_any_value/array.hpp +245 -0
  35. package/src/prelude/utils/log_any_value/config.hpp +32 -0
  36. package/src/prelude/utils/log_any_value/function.hpp +37 -0
  37. package/src/prelude/utils/log_any_value/fwd.hpp +15 -0
  38. package/src/prelude/utils/log_any_value/helpers.hpp +62 -0
  39. package/src/prelude/utils/log_any_value/log_any_value.hpp +94 -0
  40. package/src/prelude/utils/log_any_value/object.hpp +119 -0
  41. package/src/prelude/utils/log_any_value/primitives.hpp +41 -0
  42. package/src/prelude/{operators.hpp → utils/operators.hpp} +31 -12
  43. package/src/prelude/utils/well_known_symbols.hpp +13 -0
  44. package/src/prelude/values/array.hpp +5 -2
  45. package/src/prelude/{descriptors.hpp → values/descriptors.hpp} +2 -2
  46. package/src/prelude/values/function.hpp +76 -19
  47. package/src/prelude/values/{operators → helpers}/array.hpp +30 -14
  48. package/src/prelude/values/helpers/function.hpp +125 -0
  49. package/src/prelude/values/helpers/iterator.hpp +107 -0
  50. package/src/prelude/values/helpers/object.hpp +64 -0
  51. package/src/prelude/values/helpers/promise.hpp +181 -0
  52. package/src/prelude/values/helpers/string.hpp +50 -0
  53. package/src/prelude/values/helpers/symbol.hpp +23 -0
  54. package/src/prelude/values/iterator.hpp +96 -0
  55. package/src/prelude/values/object.hpp +8 -3
  56. package/src/prelude/values/promise.hpp +73 -0
  57. package/src/prelude/values/prototypes/array.hpp +23 -16
  58. package/src/prelude/values/prototypes/function.hpp +26 -0
  59. package/src/prelude/values/prototypes/iterator.hpp +58 -0
  60. package/src/prelude/values/prototypes/object.hpp +26 -0
  61. package/src/prelude/values/prototypes/promise.hpp +124 -0
  62. package/src/prelude/values/prototypes/string.hpp +366 -357
  63. package/src/prelude/values/prototypes/symbol.hpp +41 -0
  64. package/src/prelude/values/string.hpp +25 -0
  65. package/src/prelude/values/symbol.hpp +102 -0
  66. package/src/prelude/access.hpp +0 -86
  67. package/src/prelude/error.hpp +0 -31
  68. package/src/prelude/error_helpers.hpp +0 -59
  69. package/src/prelude/log_string.hpp +0 -403
  70. package/src/prelude/values/operators/function.hpp +0 -34
  71. package/src/prelude/values/operators/object.hpp +0 -34
  72. package/src/prelude/well_known_symbols.hpp +0 -10
@@ -1,13 +1,41 @@
1
1
  import ts from "typescript";
2
2
  import { CodeGenerator } from "./";
3
- export function generateLambda(node, isAssignment = false, capture = "[=]") {
3
+ export function generateLambda(node, context, options) {
4
+ const isAssignment = options?.isAssignment || false;
5
+ const capture = options?.capture || "[=]";
4
6
  const declaredSymbols = this.getDeclaredSymbols(node);
5
7
  const argsName = this.generateUniqueName("__args_", declaredSymbols);
6
- let lambda = `${capture}(const std::vector<jspp::AnyValue>& ${argsName}) mutable -> jspp::AnyValue `;
8
+ const isInsideGeneratorFunction = this.isGeneratorFunction(node);
9
+ const isInsideAsyncFunction = this.isAsyncFunction(node);
10
+ const returnCmd = this.getReturnCommand({
11
+ isInsideGeneratorFunction: isInsideGeneratorFunction,
12
+ isInsideAsyncFunction: isInsideAsyncFunction,
13
+ });
14
+ const funcReturnType = isInsideGeneratorFunction
15
+ ? "jspp::JsIterator<jspp::AnyValue>"
16
+ : (isInsideAsyncFunction ? "jspp::JsPromise" : "jspp::AnyValue");
17
+ const isArrow = ts.isArrowFunction(node);
18
+ // For generators and async functions, we MUST copy arguments because the coroutine suspends immediately
19
+ // and references to temporary arguments would dangle.
20
+ const paramThisType = (isInsideGeneratorFunction || isInsideAsyncFunction)
21
+ ? "jspp::AnyValue"
22
+ : "const jspp::AnyValue&";
23
+ const paramArgsType = (isInsideGeneratorFunction || isInsideAsyncFunction)
24
+ ? "std::vector<jspp::AnyValue>"
25
+ : "const std::vector<jspp::AnyValue>&";
26
+ const thisArgParam = isArrow
27
+ ? "const jspp::AnyValue&" // Arrow functions are never generators in this parser
28
+ : `${paramThisType} ${this.globalThisVar}`;
29
+ let lambda = `${capture}(${thisArgParam}, ${paramArgsType} ${argsName}) mutable -> ${funcReturnType} `;
30
+ const topLevelScopeSymbols = this.prepareScopeSymbolsForVisit(context.topLevelScopeSymbols, context.currentScopeSymbols);
7
31
  const visitContext = {
8
32
  isMainContext: false,
9
33
  isInsideFunction: true,
10
34
  isFunctionBody: false,
35
+ lambdaName: undefined,
36
+ topLevelScopeSymbols,
37
+ currentScopeSymbols: new Map(),
38
+ superClassVar: context.superClassVar,
11
39
  };
12
40
  if (node.body) {
13
41
  if (ts.isBlock(node.body)) {
@@ -18,14 +46,49 @@ export function generateLambda(node, isAssignment = false, capture = "[=]") {
18
46
  const defaultValue = p.initializer
19
47
  ? this.visit(p.initializer, visitContext)
20
48
  : "jspp::AnyValue::make_undefined()";
21
- paramExtraction +=
22
- `${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
49
+ if (!!p.dotDotDotToken) {
50
+ if (node.parameters.length - 1 !== i) {
51
+ throw new SyntaxError("Rest parameter must be last formal parameter.");
52
+ }
53
+ const tempName = `temp_${name}`;
54
+ paramExtraction +=
55
+ `${this.indent()}auto ${name} = jspp::AnyValue::make_undefined();\n`;
56
+ paramExtraction += `${this.indent()}{\n`;
57
+ this.indentationLevel++;
58
+ paramExtraction +=
59
+ `${this.indent()}std::vector<std::optional<jspp::AnyValue>> ${tempName};\n`;
60
+ paramExtraction +=
61
+ `${this.indent()}if (${argsName}.size() > ${i}) {\n`;
62
+ this.indentationLevel++;
63
+ paramExtraction +=
64
+ `${this.indent()}${tempName}.reserve(${argsName}.size() - ${i});\n`;
65
+ this.indentationLevel--;
66
+ paramExtraction += `${this.indent()}}\n`;
67
+ paramExtraction +=
68
+ `${this.indent()}for (size_t i = ${i}; i < ${argsName}.size(); i++) {\n`;
69
+ this.indentationLevel++;
70
+ paramExtraction +=
71
+ `${this.indent()}${tempName}.push_back(${argsName}[i]);\n`;
72
+ this.indentationLevel--;
73
+ paramExtraction += `${this.indent()}}\n`;
74
+ paramExtraction +=
75
+ `${this.indent()}${name} = jspp::AnyValue::make_array(std::move(${tempName}));\n`;
76
+ this.indentationLevel--;
77
+ paramExtraction += `${this.indent()}}\n`;
78
+ }
79
+ else {
80
+ paramExtraction +=
81
+ `${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
82
+ }
23
83
  });
24
84
  this.indentationLevel--;
25
85
  const blockContent = this.visit(node.body, {
86
+ ...visitContext,
26
87
  isMainContext: false,
27
88
  isInsideFunction: true,
28
89
  isFunctionBody: true,
90
+ isInsideGeneratorFunction: isInsideGeneratorFunction,
91
+ isInsideAsyncFunction: isInsideAsyncFunction,
29
92
  });
30
93
  // The block visitor already adds braces, so we need to inject the param extraction.
31
94
  lambda += "{\n" + paramExtraction + blockContent.substring(2);
@@ -41,10 +104,13 @@ export function generateLambda(node, isAssignment = false, capture = "[=]") {
41
104
  lambda +=
42
105
  `${this.indent()}auto ${name} = ${argsName}.size() > ${i} ? ${argsName}[${i}] : ${defaultValue};\n`;
43
106
  });
44
- lambda += `${this.indent()}return ${this.visit(node.body, {
107
+ lambda += `${this.indent()}${returnCmd} ${this.visit(node.body, {
108
+ ...visitContext,
45
109
  isMainContext: false,
46
110
  isInsideFunction: true,
47
111
  isFunctionBody: false,
112
+ isInsideGeneratorFunction: isInsideGeneratorFunction,
113
+ isInsideAsyncFunction: isInsideAsyncFunction,
48
114
  })};
49
115
  `;
50
116
  this.indentationLevel--;
@@ -52,12 +118,37 @@ export function generateLambda(node, isAssignment = false, capture = "[=]") {
52
118
  }
53
119
  }
54
120
  else {
55
- lambda += "{ return jspp::AnyValue::make_undefined(); }\n";
121
+ lambda += `{ ${returnCmd} jspp::AnyValue::make_undefined(); }\n`;
122
+ }
123
+ let signature = "";
124
+ let callable = "";
125
+ let method = "";
126
+ // Handle generator function
127
+ if (isInsideGeneratorFunction) {
128
+ signature =
129
+ "jspp::JsIterator<jspp::AnyValue>(const jspp::AnyValue&, const std::vector<jspp::AnyValue>&)";
130
+ callable = `std::function<${signature}>(${lambda})`;
131
+ method = `jspp::AnyValue::make_generator`;
132
+ } // Handle async function
133
+ else if (isInsideAsyncFunction) {
134
+ signature =
135
+ "jspp::JsPromise(const jspp::AnyValue&, const std::vector<jspp::AnyValue>&)";
136
+ callable = `std::function<${signature}>(${lambda})`;
137
+ method = `jspp::AnyValue::make_async_function`;
138
+ } // Handle normal function
139
+ else {
140
+ signature =
141
+ `jspp::AnyValue(const jspp::AnyValue&, const std::vector<jspp::AnyValue>&)`;
142
+ callable = `std::function<${signature}>(${lambda})`;
143
+ if (options?.isClass) {
144
+ method = `jspp::AnyValue::make_class`;
145
+ }
146
+ else {
147
+ method = `jspp::AnyValue::make_function`;
148
+ }
56
149
  }
57
- const signature = `jspp::AnyValue(const std::vector<jspp::AnyValue>&)`;
58
- const callable = `std::function<${signature}>(${lambda})`;
59
150
  const funcName = node.name?.getText();
60
- const fullExpression = `jspp::AnyValue::make_function(${callable}, "${funcName || ""}")`;
151
+ const fullExpression = `${method}(${callable}, "${context?.lambdaName || funcName || ""}")`;
61
152
  if (ts.isFunctionDeclaration(node) && !isAssignment && funcName) {
62
153
  return `${this.indent()}auto ${funcName} = ${fullExpression};\n`;
63
154
  }
@@ -68,12 +159,12 @@ export function visitFunctionDeclaration(node, context) {
68
159
  // This will now be handled by the Block visitor for hoisting.
69
160
  // However, we still need to generate the lambda for assignment.
70
161
  // The block visitor will wrap this in an assignment.
71
- return this.generateLambda(node);
162
+ return this.generateLambda(node, context);
72
163
  }
73
164
  return "";
74
165
  }
75
166
  export function visitArrowFunction(node, context) {
76
- return this.generateLambda(node);
167
+ return this.generateLambda(node, context);
77
168
  }
78
169
  export function visitFunctionExpression(node, context) {
79
170
  const funcExpr = node;
@@ -83,12 +174,18 @@ export function visitFunctionExpression(node, context) {
83
174
  this.indentationLevel++;
84
175
  code +=
85
176
  `${this.indent()}auto ${funcName} = std::make_shared<jspp::AnyValue>();\n`;
86
- const lambda = this.generateLambda(funcExpr, false, "[=]");
177
+ const lambda = this.generateLambda(funcExpr, {
178
+ ...context,
179
+ lambdaName: funcName,
180
+ }, {
181
+ isAssignment: true,
182
+ capture: "[=]",
183
+ });
87
184
  code += `${this.indent()}*${funcName} = ${lambda};\n`;
88
185
  code += `${this.indent()}return *${funcName};\n`;
89
186
  this.indentationLevel--;
90
187
  code += `${this.indent()}})()`;
91
188
  return code;
92
189
  }
93
- return this.generateLambda(node);
190
+ return this.generateLambda(node, context);
94
191
  }
@@ -1,14 +1,9 @@
1
1
  import ts from "typescript";
2
- import { Scope } from "../../analysis/scope";
2
+ import { BUILTIN_OBJECTS, Scope } from "../../analysis/scope";
3
+ import { DeclaredSymbolType } from "../../ast/types";
3
4
  import { CodeGenerator } from "./";
4
- const BUILTIN_OBJECTS = new Set([
5
- "global",
6
- "globalThis",
7
- "console",
8
- "Symbol",
9
- ]);
10
5
  export function isBuiltinObject(node) {
11
- return BUILTIN_OBJECTS.has(node.text);
6
+ return BUILTIN_OBJECTS.values().some((obj) => obj.name === node.text);
12
7
  }
13
8
  export function getDeclaredSymbols(node) {
14
9
  const symbols = new Set();
@@ -21,6 +16,10 @@ export function getDeclaredSymbols(node) {
21
16
  // Handles function declarations
22
17
  symbols.add(child.name.getText());
23
18
  }
19
+ else if (ts.isClassDeclaration(child) && child.name) {
20
+ // Handles class declarations
21
+ symbols.add(child.name.getText());
22
+ }
24
23
  else if (ts.isParameter(child)) {
25
24
  // Handles function parameters
26
25
  symbols.add(child.name.getText());
@@ -81,3 +80,72 @@ export function escapeString(str) {
81
80
  export function getJsVarName(node) {
82
81
  return `"${node.text}"`;
83
82
  }
83
+ export function getDerefCode(nodeText, varName, typeInfo) {
84
+ // Make sure varName is incased in quotes
85
+ if (!varName.startsWith('"'))
86
+ varName = '"' + varName;
87
+ if (!varName.endsWith('"'))
88
+ varName = varName + '"';
89
+ if (typeInfo && typeInfo.needsHeapAllocation) {
90
+ return `jspp::Access::deref_ptr(${nodeText}, ${varName})`;
91
+ }
92
+ return `jspp::Access::deref_stack(${nodeText}, ${varName})`;
93
+ }
94
+ export function getReturnCommand(context) {
95
+ return (context.isInsideGeneratorFunction || context.isInsideAsyncFunction) ? "co_return" : "return";
96
+ }
97
+ export function hoistDeclaration(decl, hoistedSymbols) {
98
+ const name = decl.name?.getText();
99
+ if (!name) {
100
+ return `/* Unknown declaration name: ${ts.SyntaxKind[decl.kind]} */`;
101
+ }
102
+ const isLetOrConst = (decl.parent.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) !==
103
+ 0;
104
+ const symbolType = isLetOrConst
105
+ ? DeclaredSymbolType.letOrConst
106
+ : (ts.isFunctionDeclaration(decl)
107
+ ? DeclaredSymbolType.function
108
+ : (ts.isClassDeclaration(decl) ? DeclaredSymbolType.letOrConst : DeclaredSymbolType.var));
109
+ if (hoistedSymbols.has(name)) {
110
+ const existingType = hoistedSymbols.get(name);
111
+ // Don't allow multiple declaration of `letOrConst` or `function` or `class` variables
112
+ if (existingType === DeclaredSymbolType.letOrConst ||
113
+ existingType === DeclaredSymbolType.function ||
114
+ existingType !== symbolType) {
115
+ throw new SyntaxError(`Identifier '${name}' has already been declared`);
116
+ }
117
+ // `var` variables can be declared multiple times
118
+ return "";
119
+ }
120
+ hoistedSymbols.set(name, symbolType);
121
+ const scope = this.getScopeForNode(decl);
122
+ const typeInfo = this.typeAnalyzer.scopeManager.lookupFromScope(name, scope);
123
+ const initializer = isLetOrConst || ts.isClassDeclaration(decl)
124
+ ? "jspp::AnyValue::make_uninitialized()"
125
+ : "jspp::AnyValue::make_undefined()";
126
+ if (typeInfo.needsHeapAllocation) {
127
+ return `${this.indent()}auto ${name} = std::make_shared<jspp::AnyValue>(${initializer});\n`;
128
+ }
129
+ else {
130
+ return `${this.indent()}jspp::AnyValue ${name} = ${initializer};\n`;
131
+ }
132
+ }
133
+ export function isGeneratorFunction(node) {
134
+ return ((ts.isFunctionDeclaration(node) ||
135
+ ts.isFunctionExpression(node) ||
136
+ ts.isMethodDeclaration(node)) &&
137
+ !!node.asteriskToken // generator indicator
138
+ );
139
+ }
140
+ export function isAsyncFunction(node) {
141
+ return ((ts.isFunctionDeclaration(node) ||
142
+ ts.isFunctionExpression(node) ||
143
+ ts.isMethodDeclaration(node) ||
144
+ ts.isArrowFunction(node)) &&
145
+ (ts.getCombinedModifierFlags(node) & ts.ModifierFlags.Async) !== 0);
146
+ }
147
+ export function prepareScopeSymbolsForVisit(topLevel, local) {
148
+ const newTopLevel = new Map(topLevel);
149
+ local.forEach((v, k) => newTopLevel.set(k, v));
150
+ return newTopLevel;
151
+ }
@@ -1,11 +1,12 @@
1
1
  import { TypeAnalyzer } from "../../analysis/typeAnalyzer";
2
2
  import { generateLambda } from "./function-handlers";
3
- import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getJsVarName, getScopeForNode, indent, isBuiltinObject, } from "./helpers";
3
+ import { escapeString, generateUniqueExceptionName, generateUniqueName, getDeclaredSymbols, getDerefCode, getJsVarName, getReturnCommand, getScopeForNode, hoistDeclaration, indent, isAsyncFunction, isBuiltinObject, isGeneratorFunction, prepareScopeSymbolsForVisit, } from "./helpers";
4
4
  import { visit } from "./visitor";
5
5
  const CONTAINER_FUNCTION_NAME = "__container__";
6
6
  export class CodeGenerator {
7
7
  indentationLevel = 0;
8
8
  typeAnalyzer;
9
+ globalThisVar;
9
10
  exceptionCounter = 0;
10
11
  // visitor
11
12
  visit = visit;
@@ -13,11 +14,17 @@ export class CodeGenerator {
13
14
  getDeclaredSymbols = getDeclaredSymbols;
14
15
  generateUniqueName = generateUniqueName;
15
16
  generateUniqueExceptionName = generateUniqueExceptionName;
17
+ hoistDeclaration = hoistDeclaration;
16
18
  getScopeForNode = getScopeForNode;
17
19
  indent = indent;
18
20
  escapeString = escapeString;
19
21
  getJsVarName = getJsVarName;
22
+ getDerefCode = getDerefCode;
23
+ getReturnCommand = getReturnCommand;
20
24
  isBuiltinObject = isBuiltinObject;
25
+ isGeneratorFunction = isGeneratorFunction;
26
+ isAsyncFunction = isAsyncFunction;
27
+ prepareScopeSymbolsForVisit = prepareScopeSymbolsForVisit;
21
28
  // function handlers
22
29
  generateLambda = generateLambda;
23
30
  /**
@@ -25,27 +32,33 @@ export class CodeGenerator {
25
32
  */
26
33
  generate(ast, analyzer) {
27
34
  this.typeAnalyzer = analyzer;
35
+ this.globalThisVar = this.generateUniqueName("__this_val__", this.getDeclaredSymbols(ast));
28
36
  const declarations = `#include "index.hpp"\n\n`;
29
37
  let containerCode = `jspp::AnyValue ${CONTAINER_FUNCTION_NAME}() {\n`;
30
38
  this.indentationLevel++;
39
+ containerCode +=
40
+ `${this.indent()}jspp::AnyValue ${this.globalThisVar} = global;\n`;
31
41
  containerCode += this.visit(ast, {
32
42
  isMainContext: true,
33
43
  isInsideFunction: true,
34
44
  isFunctionBody: true,
45
+ topLevelScopeSymbols: new Map(),
46
+ currentScopeSymbols: new Map(),
35
47
  });
36
48
  this.indentationLevel--;
37
49
  containerCode += " return jspp::AnyValue::make_undefined();\n";
38
50
  containerCode += "}\n\n";
39
51
  let mainCode = "int main() {\n";
40
- mainCode += ` std::ios::sync_with_stdio(false);\n`;
41
- mainCode += ` std::cin.tie(nullptr);\n`;
52
+ // std::ios::sync_with_stdio(false); // Removed to fix console output buffering
53
+ // std::cin.tie(nullptr); // Removed to fix console output buffering
42
54
  mainCode += ` try {\n`;
43
55
  mainCode += ` ${CONTAINER_FUNCTION_NAME}();\n`;
56
+ mainCode += ` jspp::Scheduler::instance().run();\n`;
44
57
  mainCode += ` } catch (const std::exception& ex) {\n`;
45
58
  mainCode +=
46
- " auto error = std::make_shared<jspp::AnyValue>(jspp::RuntimeError::error_to_value(ex));\n{\n";
59
+ " auto error = std::make_shared<jspp::AnyValue>(jspp::Exception::exception_to_any_value(ex));\n{\n";
47
60
  mainCode +=
48
- ` console.get_own_property("error").as_function("console.error")->call({*error});\n`;
61
+ ` ([&](){ auto __obj = console; return __obj.get_own_property("error").as_function("console.error")->call(__obj, {*error}); })();\n`;
49
62
  mainCode += ` return 1;\n}\n`;
50
63
  mainCode += ` }\n`;
51
64
  mainCode += " return 0;\n}";
@@ -30,3 +30,6 @@ export function visitUndefinedKeyword() {
30
30
  export function visitNullKeyword() {
31
31
  return "jspp::AnyValue::make_null()";
32
32
  }
33
+ export function visitThisKeyword() {
34
+ return `${this.globalThisVar}`;
35
+ }